For these exercises, you will be performing differential expression
analysis on the same set of rhabdomyosarcoma (RMS) samples we saw in the
differential expression instruction notebook. This time, however, you’ll
be testing a separate population of cells, the tumor mesoderm cells, for
differentially expressed genes between alveolar RMS (ARMS) and embryonal
RMS (ERMS) samples.
Before beginning, let’s take a moment to consider the context for
this data. Patel
et al. (2022) employed scRNA-seq and snRNA-seq to explore
the transcriptomic landscape of RMS, with a particular interest in
identifying aspects of RMS’ developmental program that can be exploited
by therapeutics. One key result from this study was that ERMS featured a
clear developmental hierarchy that ARMS lacked, suggesting there may be
different contributors to treatment resistance between subtypes.
Although development for all RMS samples mirrored normal myogenesis
(shown below) with cell types of all stages, ARMS samples had
significantly fewer tumor mesoderm cells compared to ERMS samples. Given
this difference in abundance, could there also be differences in gene
expression in this cell population? We’ll be exploring that question in
this notebook!
Figure 1B from Patel et al.
(2022)
In this exercise notebook you will:
- Part A: Prepare single-cell data for differential expression (DE)
analysis
- Part B: Perform DE analysis on the tumor mesoderm cell
subpopulation
- Part C: Compare results to tumor myoblast DE analysis results
Part A: Prepare single-cell data for differential expression (DE)
analysis
Read in and prepare the data
To begin, load some libraries that we’ll need for these exercises,
including…
SingleCellExperiment for working with SCE objects
DESeq2 for performing differential expression
ggplot2 for plotting
# Load libraries
library(SingleCellExperiment)
library(DESeq2)
library(ggplot2)
Next, set the random seed for reproducibility:
In the next chunk, we’ll set up some file paths, including the path
to our RMS data and the output paths for our differential expression
analysis results, including both the mesoderm analysis we’ll perform
here and the myoblast analysis we performed during instruction.
# data directory for input RMS data
data_dir <- file.path("data", "rms")
# file containing just the samples to use for DE analysis,
# as created during instruction
rms_sce_file <- file.path(data_dir, "integrated", "rms_subset_sce.rds")
# ensure analysis results directory has been created
# it should exist already from instruction
deseq_dir <- file.path("analysis", "rms", "deseq")
fs::dir_create(deseq_dir)
# File where we will output results from mesoderm DE analysis
deseq_mesoderm_file <- file.path(deseq_dir, "rms_mesoderm_deseq_results.tsv")
# File where we previously outputted results myoblast DE analysis during
# instruction
deseq_myoblast_file <- file.path(deseq_dir, "rms_myoblast_deseq_results.tsv")
Now that we’ve defined our file paths, we can read some of it in.
First, let’s read in the deseq_myoblast_file TSV and
save it to deseq_myoblast_results. We’ll need this later
when we compare mesoderm DE results to myoblast DE results.
# read in myoblast de results for later
deseq_myoblast_results <- readr::read_tsv(deseq_myoblast_file)
Next, read in the SCE file and save it to a variable called
rms_sce. Then, print it out to have a look at its
contents.
# read in sce object
rms_sce <- readRDS(rms_sce_file)
# print sce
rms_sce
class: SingleCellExperiment
dim: 60319 26033
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(3): counts logcounts fastmnn_corrected
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(26033): SCPCL000479-GGGACCTCAAGCGGAT SCPCL000479-CACAGATAGTGAGTGC ... SCPCL000491-TCGCACTAGGAACGTC SCPCL000491-TTGCATTTCAACGCTA
colData names(24): sum detected ... cell_id diagnosis_group
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):
Before we proceed, this SCE object contains a colData
column sizeFactor that was previously calculated when
counts were normalized during initial processing. This column will end
up conflicting with some of DESeq2’s operations, so we want
to remove this column before proceeding. (Note that if you skipped this
step, DESeq2 would end up printing an error about this
offending column specifically, so you would have found out later that it
has to go!)
# Set column value to `NULL` in order to remove it
rms_sce$sizeFactor <- NULL
As we saw during instruction, this SCE contains three ERMS and three
ARMS samples. Let’s remind ourselves of the distribution of cell types
before proceeding. To focus on the tumor cell types (as we will be doing
for the rest of the notebook!), we’ll also subset the
rms_sce object to just tumor cells.
tumor_cells <- rms_sce$celltype_broad |>
# Find which cell types start with "Tumor_"
stringr::str_starts("Tumor_") |>
# Pipe into `which()` to ensure that `NA` values are properly handled
which()
# filter the SCE object to keep only those cells
rms_sce <- rms_sce[, tumor_cells]
Let’s now plot the distribution of the remaining tumor cell
types:
# Create a data frame version of the colData table for plotting
coldata_df <- colData(rms_sce) |>
as.data.frame()
# Make a barplot of cell type proportions across samples and diagnoses
ggplot(coldata_df) +
aes(x = sample,
fill = celltype_broad) +
geom_bar(color = "black", # Add a thin outline for easier viewing
lwd = 0.15) +
facet_wrap(vars(diagnosis_group),
# only put relevant samples in each panel
scales = "free_x") +
# Specify theme and modify x-axis text size
theme_bw() +
theme(axis.text.x = element_text(size = 7))

We can see that the proportion of Tumor_Myoblast is much
higher than other cell types, including the Tumor_Mesoderm
subpopulations we’ll be testing here. We also see that ERMS samples have
more Tumor_Mesoderm cells and fewer
Tumor_Myocyte cells compared to ARMS samples, consistent
with Patel et al.’s findings that ARMS samples tended to have
more cell types from later myogenesis developmental stages compared to
ERMS. As we proceed to the pseudo-bulking steps for the
Tumor_Mesoderm subpopulation, we will check that we have
sufficient sample sizes to perform our analysis.
Perform pseudo-bulking
To begin differential expression on single-cell data, we need to
pseudo-bulk the data to “collapse” groups of interest. As during
instruction, we want to perform pseudo-bulking across two groupings:
celltype_broad and sample.
To accomplish this, create a variable called pb_groups,
which will be a DataFrame that contains only the
colData columns of interest for pseudo-bulking.
# first subset the coldata
# to only have the columns we care about for pseudo-bulking
pb_groups <- colData(rms_sce)[,c("celltype_broad", "sample")]
Next, use this variable to create the pseudo-bulked SCE with the
scuttle::aggregateAcrossCells() function, and save it to
pb_sce. In addition, you’ll want to ensure column names are
properly added back into this new pseudo-bulked object.
# create a new SCE object that contains
# the pseudo-bulked counts across the provided groups
pb_sce <- scuttle::aggregateAcrossCells(
rms_sce,
id = pb_groups
)
# Add column names formatted as `{celltype}_{sample}` back to `pb_sce`
colnames(pb_sce) <- glue::glue("{pb_sce$celltype_broad}_{pb_sce$sample}")
# Check
pb_sce
class: SingleCellExperiment
dim: 60319 18
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(1): counts
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(18): Tumor_Mesoderm_SCPCL000479 Tumor_Mesoderm_SCPCL000480 ... Tumor_Myocyte_SCPCL000488 Tumor_Myocyte_SCPCL000491
colData names(26): sum detected ... sample ncells
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):
Let’s see what we’ve got! Print out pb_sce below. How
many “cells” do we have now after pseudo-bulking?
pb_sce
class: SingleCellExperiment
dim: 60319 18
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(1): counts
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(18): Tumor_Mesoderm_SCPCL000479 Tumor_Mesoderm_SCPCL000480 ... Tumor_Myocyte_SCPCL000488 Tumor_Myocyte_SCPCL000491
colData names(26): sum detected ... sample ncells
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):
As we have seen, when we performed the pseudo-bulking, a new column
ncells was added to the colData table. This
information tells us how many cells were collapsed into the
grouping.
To perform our differential expression analysis on
Tumor_Mesoderm cells, we’d like to see a suitable number of
cells present in each group. During instruction, we chose a threshold of
ncells >= 10 to include a group in analysis. Let’s do
the same filtering here, and then check to see that we have enough
biological replicates to perform analysis. Recall, we are starting with
three ERMS and three ARMS samples, and we’ll need at least three samples
from each diagnosis group for our cell type of interest for statistical
viability.
To this end, let’s take two filtering steps in the next chunk, saving
the output to a new object called mesoderm_pb_sce:
- We’ll filter the
pb_sce to retain only groupings where
ncells >= 10
- We’ll also filter the
pbe_sce object to retain only our
cell type of interest, Tumor_Mesoderm.
# First filter to keep only `ncells >= 10` and save to `mesoderm_pb_sce`
mesoderm_pb_sce <- pb_sce[, which(pb_sce$ncells >= 10)]
# Then filter to keep only `Tumor_Mesoderm` and save to the same variable
# First create logical vector
mesoderm_cells <- pb_sce$celltype_broad == "Tumor_Mesoderm"
mesoderm_pb_sce <- mesoderm_pb_sce[,mesoderm_cells]
Now let’s check that we have retained enough samples to proceed. In
the following chunk, use the table() function on the
filtered SCE’s diagnosis_group to count up the number of
each diagnosis - hopefully we still have at least three (pseudo-bulked)
samples of each!
# Make a table of the diagnosis group counts
table(mesoderm_pb_sce$diagnosis_group)
Indeed, we have enough biological replicates to proceed, so let’s
head to differential expression analysis.
Part B: Perform DE analysis on the tumor mesoderm cell
subpopulation
Off to (the) differential expression (races)!
Now that we have prepared our object for analysis, we’re ready to
identify genes that are differentially expressed between ARMS and ERMS
in Tumor_Mesoderm cells.
First, let’s define a DESeqDataSet object called
deseq_object from the mesoderm_pb_sce,
specifying the design formula across the diagnosis_group
variable.
# create the DESeqDataSet object
deseq_object <- DESeqDataSet(
mesoderm_pb_sce,
design = ~diagnosis_group
)
Now, use the convenience function DESeq2::DESeq() to
perform the differential expression analysis as we saw during
instruction, saving the result back to deseq_object.
# perform differential expression
deseq_object <- DESeq2::DESeq(deseq_object)
Before proceeding, we’ll want to quickly check that the model was a
decent fit to the data, and we can do this by visualizing the dispersion
estimates using the function DESeq2::plotDispEsts(). Make
this plot in the chunk below:
# plot dispersions
DESeq2::plotDispEsts(deseq_object)

How does this plot look to you? A well-fitted model should show
decreasing dispersions as the mean along the x-axis increases. Do you
see that trend here?
To be able to interpret our results and compare genes, we next need
to apply a shrinkage procedure that will correct expression levels to
avoid overestimating fold change. Do this in the next chunk by taking
the following steps:
- Extract the results which pass a threshold of
alpha = 0.05 from deseq_object. Save this data
frame to a variable deseq_results.
- Perform the shrinkage procedure using
DESeq2::lfcShrink(), saving results to
shrink_results. Make sure to look at your model first to
find the position of the result coefficient to provide to
DESeq2::lfcShrink().
# GEW: I'm curious about 2 things:
# 1) To what extent cell proportion / count impacts DE results -- subset to 400 random cells (Tumor_mesoderm) per diagnosis group, and repeat analysis
# 2) To what extent shrinkage impacts DE results -- repeat visualizations on the non-shrunk results
# Extract results into a data frame
deseq_results <- DESeq2::results(deseq_object, alpha = 0.05)
# Before performing the shrinkage procedure, check
# here to find where the result coefficient is
DESeq2::resultsNames(deseq_object)
[1] "Intercept" "diagnosis_group_ERMS_vs_ARMS"
# Perform the shrinkage procedure, saving to `shrink_results`
shrink_results <- DESeq2::lfcShrink(
deseq_object,
res = deseq_results,
coef = 2,
type = "apeglm"
)
# Compare
head(deseq_results)
log2 fold change (MLE): diagnosis group ERMS vs ARMS
Wald test p-value: diagnosis group ERMS vs ARMS
DataFrame with 6 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric> <numeric>
ENSG00000000003 8.713223 0.649856 0.986484 0.658760 0.510050 0.820792
ENSG00000000005 0.730183 1.068391 2.932840 0.364286 0.715645 NA
ENSG00000000419 30.176049 0.225685 0.602494 0.374584 0.707970 0.908990
ENSG00000000457 19.815192 -0.296812 0.759054 -0.391029 0.695776 0.903512
ENSG00000000460 27.162696 0.610321 0.655246 0.931439 0.351626 0.712684
ENSG00000000938 0.544495 0.496746 2.671682 0.185930 0.852500 NA
head(shrink_results)
log2 fold change (MAP): diagnosis group ERMS vs ARMS
Wald test p-value: diagnosis group ERMS vs ARMS
DataFrame with 6 rows and 5 columns
baseMean log2FoldChange lfcSE pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSG00000000003 8.713223 0.1855118 0.557615 0.510050 0.820792
ENSG00000000005 0.730183 0.0665971 0.619273 0.715645 NA
ENSG00000000419 30.176049 0.1181973 0.440999 0.707970 0.908990
ENSG00000000457 19.815192 -0.1203426 0.489972 0.695776 0.903512
ENSG00000000460 27.162696 0.3128435 0.491756 0.351626 0.712684
ENSG00000000938 0.544495 0.0527327 0.614362 0.852500 NA
Go ahead and print the resulting shrink_results:
shrink_results
Before proceeding, it’s worth discussing how to interpret these
log2FoldChange values in terms of ARMS and ERMS samples. In
this case, we did not set a particular factor order for the
diagnosis_group variable that we tested on, so R by default
set the order alphabetically. We can see this below using
levels().
# See the order of the diagnosis_group column
levels(mesoderm_pb_sce$diagnosis_group)
[1] "ARMS" "ERMS"
This means that all results are relative to ARMS. Negative
log2FoldChange values indicate that ERMS has lower
expression relative to ARMS. Positive log2FoldChange values
indicate that ERMS has higher expression relative to ARMS.
Something else we’ll need to help us interpret these results are some
gene names, since not all of us have had a chance to memorize the
Ensembl IDs yet. So, let’s now add in a gene_symbol column
to our results data frame (shrink_results). These symbols
were originally stored in the SCE’s rowData table so we can
simply grab the names from there. In the chunk below, we’ll do some
wrangling to combine shrink_results with the gene names,
saving the final result to deseq_mesoderm_results.
# Create a data frame from the SCE rowData
sce_rowdata_df <- rowData(mesoderm_pb_sce) |>
# Convert to a data frame and move the rownames
# into a new column called `ensembl_id`
tibble::as_tibble(rownames = "ensembl_id")
# Join the DESeq results with `sce_rowdata_df`
deseq_mesoderm_results <- shrink_results |>
tibble::as_tibble(rownames = "ensembl_id") |>
# join with sce_rowdata_df by Ensembl ids
dplyr::left_join(sce_rowdata_df, by = "ensembl_id") |>
# reorder columns so gene_symbol comes right after ensembl_id
# to make the table easier to read
dplyr::relocate(gene_symbol, .after = "ensembl_id")
# Print the final results data frame
deseq_mesoderm_results
Let’s now filter to only our significant results, using a cutoff of
adjusted P-value of 0.05, and save the new data frame to
deseq_mesoderm_results_sig
# Filter results to padj <= 0.05
deseq_mesoderm_results_sig <- deseq_mesoderm_results[which(deseq_mesoderm_results$padj <= 0.05),]
# Print the result
deseq_mesoderm_results_sig
# Another method using dplyr
deseq_mesoderm_results_sig2 <- deseq_mesoderm_results |>
dplyr::filter(padj <= 0.05)
deseq_mesoderm_results_sig2
Visualize results
Let’s visualize some of our results!
To start, use the EnhancedVolcano package to make a
volcano plot of the results. For this plot, specify that points be
labeled with the gene symbol and use the default P-value cutoff of
1e-05. Feel free to apply any additional styles you want
(the plot itself is a ggplot2 object!). Hint: Make sure to
plot the deseq_mesoderm_results data frame that contains
both significant and not significant results.
# Make an (enhanced) volcano plot
shrinkage_volcano_plot <- EnhancedVolcano::EnhancedVolcano(
deseq_mesoderm_results,
x = "log2FoldChange",
y = "padj",
lab = deseq_mesoderm_results$gene_symbol,
pCutoff = 1e-05, # p value cutoff (default)
drawConnectors = TRUE,
labSize = 3,
title = "DESeq2 Tumor Mesoderm (With Shrinkage)", # no title
subtitle = NULL, # or subtitle
caption = NULL, # or caption
) +
theme_classic() +
theme(legend.position = "bottom")
shrinkage_volcano_plot
ggsave(glue::glue("{deseq_dir}/volcano_shrinkage.png"), dpi=400, height=5, width=5)

Let’s pick some highly differentially expressed genes from this
volcano plot and show them on a UMAP. We’ll pick one gene that shows
higher ARMS expression (negative log2 fold changes), and one that shows
higher ERMS expression (positive log2 fold changes). To plot these on a
UMAP, we’ll need to know their Ensembl ids, so let’s get that
information first:
# Define some genes to visualize
arms_gene <- "HMCN2"
erms_gene <- "PLEKHB2"
# Use dplyr to get the associated Ensembl ids
deseq_mesoderm_results_sig |>
# Find rows that contain our gene symbols
dplyr::filter(gene_symbol %in% c(erms_gene, arms_gene)) |>
# Select only these two columns
dplyr::select(ensembl_id, gene_symbol)
To make our UMAP of mesoderm cells, we also want to filter our
original rms_sce to contain only
Tumor_Mesoderm cells. (An aside! You may recall we
previously created mesoderm_pb_sce that was filtered to
tumor mesoderm cells, so why can’t we use that object to make a UMAP?
That object was derived from the pseudo-bulked SCE, so it does
not contain individual cells. Therefore, we need to go back to the
beginning and work with rms_sce.)
# Filter rms_sce to contain only Tumor_Mesoderm cells
mesoderm_sce <- rms_sce[,which(rms_sce$celltype_broad == 'Tumor_Mesoderm')]
We should also check the names of our reduced dimensions to see what
the UMAP matrix is called so that we can plot it:
# Print reducedDimNames of mesoderm_sce
reducedDimNames(mesoderm_sce)
[1] "PCA" "UMAP" "fastmnn_PCA" "fastmnn_UMAP"
We’d like to plot the integrated UMAP, so we’ll plot the
"fastmnn_UMAP" matrix below.
I map, you map, we all map for map maps! First, let’s visualize the
gene with Ensembl id ENSG00000148357 that is upregulated in
ARMS. For this plot, color by expression and facet by
diagnosis_group.
# Plot UMAP showing ENSG00000148357 expression across diagnosis groups
scater::plotUMAP(
mesoderm_sce,
dimred = "fastmnn_UMAP",
color_by = "ENSG00000148357",
other_fields = "diagnosis_group"
) +
theme_bw() +
facet_grid(cols = vars(diagnosis_group))

How does this plot look to you? Does it seem to reflect the
statistical result that ARMS has higher expression, on average, compared
to ERMS?
Let’s do the same with the gene that is upregulated in ERMS, and
again think about whether the plot shows the trends you expected based
on the differential expression results!
# GEW: What's the difference between facet_wrap() and facet_grid() ?
# Plot UMAP showing ENSG00000115762 expression across diagnosis groups
scater::plotReducedDim(mesoderm_sce,
dimred = "fastmnn_UMAP",
color_by = "ENSG00000115762",
other_fields = "diagnosis_group") +
facet_wrap(vars(diagnosis_group)) +
theme_bw()

We can also visualize this data using violin plots with the
scater::plotExpression() function, allowing us to compare
expression across subpopulations of interest (here, tumor cell types and
diagnosis groups).
Let’s make that plot below from the rms_sce object,
which you’ll recall we previously filtered to only tumor cells. Use
facet_grid() in your plot below to show separate panels for
each combination of cell type and diagnosis group.
# Define a vector of Ensembl ids to plot
# Create faceted violin plot
Based on this plot, it looks like the expression differences among
diagnosis groups we found for tumor mesoderm cells could be an
overall trend across cancer cell types. If we wanted to formally test
this, we could consider a multivariate model that accounts for both cell
type and diagnosis group at once, rather than performing a separately
testing each cell type.
Let’s go ahead and save the deseq_mesoderm_results data
frame to our output TSV file.
# Write out the results TSV file
Part C: Compare results to tumor myoblast DE analysis results
During instruction, we performed differential expression analysis on
tumor myoblast cells to detect differences between ARMS and ERMS
samples. We might wonder how our results for tumor mesoderm cells
compare to those for myoblasts. Before we compare some of these
findings, we need to be aware of a few caveats:
As mentioned above, if we were really interested in whether ARMS
and ERMS samples have different expression across any group of cell
types, we would have liked to start with a multivariate model. By
performing separate tests for separate cell types, we risk making
comparisons based on “significance,” for example concluding “this gene
was significant in mesoderm but not myoblast, therefore it’s only
important in mesoderm cell types.” Significance as a property
in-and-of-itself relies on arbitrary P-value cutoffs, and P-values
across different tests and samples are not created equally! This means
you do not want to rely on these kinds of comparisons to draw
conclusions, but they can be useful for exploration, as we will do
here.
During all of our DE tests, there’s a good chance that we may
have committed some light circularity: The cell types we are using from
Patel et
al. (2022) were determined from expression data. But then we
used these expression-derived cell types to filter for DE testing, and
then we tested for differences in, you guessed it, expression! It’s not
necessarily possible to avoid this trap (since we need to use
something to annotate cell types), but it’s important to be
aware that this is a confounding factor across our results.
Bearing these caveats in mind, let’s go ahead and compare results a
bit. At the beginning of this notebook, we read in the tumor myoblast DE
results and saved it as deseq_myoblast_results. Let’s
remind ourselves what this data frame looks like:
head(deseq_myoblast_results)
The data frame has the same columns as our mesoderm results stored in
deseq_mesoderm_results. To compare results more easily,
we’d like to have them all in a single data frame formatted to
facilitate comparison. For example, we can make a data frame that has
columns for each test’s P-values and log2-fold changes so we can
directly compare the results for each gene. Let’s wrangle that
together:
# select from and rename myoblast results
myoblast_df <- deseq_myoblast_results |>
dplyr::select(ensembl_id,
gene_symbol,
myoblast_padj = padj,
myoblast_log2FoldChange = log2FoldChange)
# select & rename mesoderm result
mesoderm_df <- deseq_mesoderm_results |>
dplyr::select(ensembl_id,
gene_symbol,
mesoderm_padj = padj,
mesoderm_log2FoldChange = log2FoldChange)
# Combine myoblast and mesoderm results
deseq_results_all <- dplyr::inner_join(myoblast_df, mesoderm_df)
# Print the combined result data frame
deseq_results_all
We can now directly compare results on a per-gene basis! One way we
can explore the result differences is with a scatterplot showing
log2-fold changes for each cell type. We can also style the points to be
colored based on their overall significance, using one color for each of
the following conditions:
- Both cell types are significant
- Only myoblast tumor cells are significant
- Only mesoderm tumor cells are significant
- Neither cell type is significant
Coloring points by significance in ggplot2 will require
a bit more wrangling, for which we’ll use the function dplyr::case_when().
We provide this function with a set of logical expressions and each
assigned value is designated by ~. The expressions are
evaluated in order, stopping at the first one that evaluates as
TRUE and returning the associated value. We’ll use
dplyr::case_when() to help us create a new
significance_type column in our data frame to contain this
information.
deseq_results_all <- deseq_results_all |>
# Create a new column called significance_type
dplyr::mutate(significance_type = dplyr::case_when(
# Use value "Both" if both comparisons are significant
myoblast_padj <= 0.05 & mesoderm_padj <= 0.05 ~ "Both",
# Use value "Myoblast only" if only myoblast is significant
myoblast_padj <= 0.05 ~ "Myoblast only",
# Use value "Mesoderm only" if only mesoderm is significant
mesoderm_padj <= 0.05 ~ "Mesoderm only",
# Default case: neither comparison is significant
# This will capture all remaining rows not covered by
# the previous conditions
TRUE ~ "Neither"
)) |>
# Move the new column to the front of the data frame
# so we can easily see it when printing
dplyr::select(significance_type, everything())
# Print the updated data frame
deseq_results_all
Let’s have a quick look at the new significance_type
variable by making a table() of its values:
# Make a table of significance types
Most genes are not significant in either comparison, and the myoblast
test detected more significant genes compared to the mesoderm test. But
there is still quite a bit of overlap in the genes identified as
significant in both tests!
Now we can make our scatterplot:
# Plot log2 fold changes against each other
ggplot(deseq_results_all,
aes(x = myoblast_log2FoldChange,
y = mesoderm_log2FoldChange,
color = significance_type)) +
# Styling to see points
geom_point(size = 0.5,
alpha = 0.5) +
# Use the colorblind-friendly viridis color scale
scale_color_viridis_d() +
# Set plot labels
labs(
x = "Myoblast log2 fold change",
y = "Mesoderm log2 fold change",
color = "Signficance Type"
) +
theme_bw() +
# Make legend points larger and easier to see
guides(color = guide_legend(override.aes = list(size = 2))) +
# Set equal x and y scales for easier comparison between axes
coord_equal()
What kind of trends do we see here?
- The middle of the plot close to coordinates
(0,0) has a
large cloud of yellow “Neither” points, which makes sense.
- Along each line
x = 0 or y = 0, we see
strips of points for genes that are only significant for only one cell
type. For example, the blue “Mesoderm only” points mostly follow
x = 0; for these points, the associated myoblast log2-fold
changes are all at about 0.
- Finally, we have the purple “Both” points, which are present in each
quadrant farther away from
(0,0), deemed significant in
both DE tests. Most of these points are in the top right and bottom left
quadrants, corresponding to conditions where both cell types
show significant upregulation or downregulation, respectively. However,
we do also see some purple points, albeit fewer, in the top left and
bottom right quadrants which represent genes where both cell types show
significant expression differences but in different
directions.
To start exploring, let’s pick out some of the genes with the largest
fold-changes from the top right and bottom left quadrants of the plot.
Based on the plot, it looks like we’ll find a set of shared upregulated
genes by looking for expression values that are greater than 8 for both
cell types, and downregulated genes by looking for expression values
less than -10 for myoblasts and -7 for mesoderm cells.
# Find genes in top right and bottom left quadrants
deseq_results_all |>
dplyr::filter(
# Find either the highly upregulated genes:
(myoblast_log2FoldChange >= 8 & mesoderm_log2FoldChange >= 8) |
# OR, the highly downregulated genes:
(myoblast_log2FoldChange <= -10 & mesoderm_log2FoldChange <=-7)
) |>
# arrange on myoblast_log2FoldChange
dplyr::arrange(myoblast_log2FoldChange)
Something you’ll see in these results are some pretty different
P-values between cell types (also note that the NA genes
here are lncRNAs with no formally assigned gene symbol). Specifically,
the myoblast P-values are all 3-7 orders of magnitude lower than their
mesoderm counterparts, which may be a result of the relatively
higher sample size for myoblast tests - larger sample sizes lead to more
extreme P-values. Importantly, we do not want to compare these
P-values directly and conclude that a given gene was “more or less
significant” in one cell type or another, since P-values cannot be
compared across tests (again, for a more robust assessment of
differential expression, use a multivariate model that accounts for cell
types!).
To wrap up, feel free to perform some quick visualization of some of
these genes!
Alternatively (or in addition!), you can visualize the differential
expression of genes that regulate myogenesis, as shown in the figure
from Patel et al. at the top of this notebook. Let’s have a
quick look at those results:
# Define vector of myogenesis gene symbols
myogenesis_symbols <- c("MEOX2", "PAX3", "MYF5", "MSC", "MYOG", "MEF2C")
# Print the DE results for those genes
deseq_results_all |>
dplyr::filter(gene_symbol %in% myogenesis_symbols) |>
# sort in order of the `myogenesis_symbols` vector
dplyr::arrange(match(gene_symbol, myogenesis_symbols))
You can make plots we’ve seen like…
- A faceted UMAP with
scater::plotUMAP() showing
expression across cell type and diagnosis group
- A violin plot with
scater::plotExpression() faceted
across cell type and diagnosis group
# Use this chunk (or add more chunks!) to make your own plots
# vector of gene symbols named by Ensembl id
Session Info
sessionInfo()
LS0tCnRpdGxlOiAiU2luZ2xlIENlbGwgRXhlcmNpc2U6IERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgoKRm9yIHRoZXNlIGV4ZXJjaXNlcywgeW91IHdpbGwgYmUgcGVyZm9ybWluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiB0aGUgc2FtZSBzZXQgb2YgcmhhYmRvbXlvc2FyY29tYSAoUk1TKSBzYW1wbGVzIHdlIHNhdyBpbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaW5zdHJ1Y3Rpb24gbm90ZWJvb2suClRoaXMgdGltZSwgaG93ZXZlciwgeW91J2xsIGJlIHRlc3RpbmcgYSBzZXBhcmF0ZSBwb3B1bGF0aW9uIG9mIGNlbGxzLCB0aGUgdHVtb3IgbWVzb2Rlcm0gY2VsbHMsIGZvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYmV0d2VlbiBhbHZlb2xhciBSTVMgKEFSTVMpIGFuZCBlbWJyeW9uYWwgUk1TIChFUk1TKSBzYW1wbGVzLgoKCkJlZm9yZSBiZWdpbm5pbmcsIGxldCdzIHRha2UgYSBtb21lbnQgdG8gY29uc2lkZXIgdGhlIGNvbnRleHQgZm9yIHRoaXMgZGF0YS4KW1BhdGVsIF9ldCBhbC5fICgyMDIyKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5kZXZjZWwuMjAyMi4wNC4wMDMpIGVtcGxveWVkIHNjUk5BLXNlcSBhbmQgc25STkEtc2VxIHRvIGV4cGxvcmUgdGhlIHRyYW5zY3JpcHRvbWljIGxhbmRzY2FwZSBvZiBSTVMsIHdpdGggYSBwYXJ0aWN1bGFyIGludGVyZXN0IGluIGlkZW50aWZ5aW5nIGFzcGVjdHMgb2YgUk1TJyBkZXZlbG9wbWVudGFsIHByb2dyYW0gdGhhdCBjYW4gYmUgZXhwbG9pdGVkIGJ5IHRoZXJhcGV1dGljcy4KT25lIGtleSByZXN1bHQgZnJvbSB0aGlzIHN0dWR5IHdhcyB0aGF0IEVSTVMgZmVhdHVyZWQgYSBjbGVhciBkZXZlbG9wbWVudGFsIGhpZXJhcmNoeSB0aGF0IEFSTVMgbGFja2VkLCBzdWdnZXN0aW5nIHRoZXJlIG1heSBiZSBkaWZmZXJlbnQgY29udHJpYnV0b3JzIHRvIHRyZWF0bWVudCByZXNpc3RhbmNlIGJldHdlZW4gc3VidHlwZXMuCkFsdGhvdWdoIGRldmVsb3BtZW50IGZvciBhbGwgUk1TIHNhbXBsZXMgbWlycm9yZWQgbm9ybWFsIG15b2dlbmVzaXMgKHNob3duIGJlbG93KSB3aXRoIGNlbGwgdHlwZXMgb2YgYWxsIHN0YWdlcywgQVJNUyBzYW1wbGVzIGhhZCBzaWduaWZpY2FudGx5IGZld2VyIHR1bW9yIG1lc29kZXJtIGNlbGxzIGNvbXBhcmVkIHRvIEVSTVMgc2FtcGxlcy4KR2l2ZW4gdGhpcyBkaWZmZXJlbmNlIGluIGFidW5kYW5jZSwgY291bGQgdGhlcmUgYWxzbyBiZSBkaWZmZXJlbmNlcyBpbiBnZW5lIGV4cHJlc3Npb24gaW4gdGhpcyBjZWxsIHBvcHVsYXRpb24/CldlJ2xsIGJlIGV4cGxvcmluZyB0aGF0IHF1ZXN0aW9uIGluIHRoaXMgbm90ZWJvb2shCgoKIVtGaWd1cmUgMUIgZnJvbSBQYXRlbCBfZXQgYWwuXyAoMjAyMildKGRpYWdyYW1zL3BhdGVsXzIwMjJfZmlnMUIucG5nKQoKCkluIHRoaXMgZXhlcmNpc2Ugbm90ZWJvb2sgeW91IHdpbGw6CgotIFBhcnQgQTogUHJlcGFyZSBzaW5nbGUtY2VsbCBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGFuYWx5c2lzCi0gUGFydCBCOiBQZXJmb3JtIERFIGFuYWx5c2lzIG9uIHRoZSB0dW1vciBtZXNvZGVybSBjZWxsIHN1YnBvcHVsYXRpb24KLSBQYXJ0IEM6IENvbXBhcmUgcmVzdWx0cyB0byB0dW1vciBteW9ibGFzdCBERSBhbmFseXNpcyByZXN1bHRzCgoKIyMgUGFydCBBOiBQcmVwYXJlIHNpbmdsZS1jZWxsIGRhdGEgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIChERSkgYW5hbHlzaXMKCiMjIyBSZWFkIGluIGFuZCBwcmVwYXJlIHRoZSBkYXRhCgpUbyBiZWdpbiwgbG9hZCBzb21lIGxpYnJhcmllcyB0aGF0IHdlJ2xsIG5lZWQgZm9yIHRoZXNlIGV4ZXJjaXNlcywgaW5jbHVkaW5nLi4uCgotIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgZm9yIHdvcmtpbmcgd2l0aCBTQ0Ugb2JqZWN0cwotIGBERVNlcTJgIGZvciBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCi0gYGdncGxvdDJgIGZvciBwbG90dGluZwoKYGBge3IgbG9hZCBsaWJyYXJpZXMsIHNvbHV0aW9uID0gVFJVRX0KIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3Bsb3QyKQpgYGAKCk5leHQsIHNldCB0aGUgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eToKCmBgYHtyIHNldCBzZWVkLCBzb2x1dGlvbiA9IFRSVUV9CiMgU2V0IHRoZSByYW5kb20gc2VlZApzZXQuc2VlZCgyOTgzKQpgYGAKCkluIHRoZSBuZXh0IGNodW5rLCB3ZSdsbCBzZXQgdXAgc29tZSBmaWxlIHBhdGhzLCBpbmNsdWRpbmcgdGhlIHBhdGggdG8gb3VyIFJNUyBkYXRhIGFuZCB0aGUgb3V0cHV0IHBhdGhzIGZvciBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0cywgaW5jbHVkaW5nIGJvdGggdGhlIG1lc29kZXJtIGFuYWx5c2lzIHdlJ2xsIHBlcmZvcm0gaGVyZSBhbmQgdGhlIG15b2JsYXN0IGFuYWx5c2lzIHdlIHBlcmZvcm1lZCBkdXJpbmcgaW5zdHJ1Y3Rpb24uCgoKYGBge3IgZmlsZXBhdGhzfQojIGRhdGEgZGlyZWN0b3J5IGZvciBpbnB1dCBSTVMgZGF0YQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAicm1zIikKCiMgZmlsZSBjb250YWluaW5nIGp1c3QgdGhlIHNhbXBsZXMgdG8gdXNlIGZvciBERSBhbmFseXNpcywKIyAgYXMgY3JlYXRlZCBkdXJpbmcgaW5zdHJ1Y3Rpb24Kcm1zX3NjZV9maWxlIDwtIGZpbGUucGF0aChkYXRhX2RpciwgImludGVncmF0ZWQiLCAgInJtc19zdWJzZXRfc2NlLnJkcyIpCgojIGVuc3VyZSBhbmFseXNpcyByZXN1bHRzIGRpcmVjdG9yeSBoYXMgYmVlbiBjcmVhdGVkCiMgaXQgc2hvdWxkIGV4aXN0IGFscmVhZHkgZnJvbSBpbnN0cnVjdGlvbgpkZXNlcV9kaXIgPC0gZmlsZS5wYXRoKCJhbmFseXNpcyIsICJybXMiLCAiZGVzZXEiKQpmczo6ZGlyX2NyZWF0ZShkZXNlcV9kaXIpCgojIEZpbGUgd2hlcmUgd2Ugd2lsbCBvdXRwdXQgcmVzdWx0cyBmcm9tIG1lc29kZXJtIERFIGFuYWx5c2lzCmRlc2VxX21lc29kZXJtX2ZpbGUgPC0gZmlsZS5wYXRoKGRlc2VxX2RpciwgInJtc19tZXNvZGVybV9kZXNlcV9yZXN1bHRzLnRzdiIpCgojIEZpbGUgd2hlcmUgd2UgcHJldmlvdXNseSBvdXRwdXR0ZWQgcmVzdWx0cyBteW9ibGFzdCBERSBhbmFseXNpcyBkdXJpbmcKIyAgaW5zdHJ1Y3Rpb24KZGVzZXFfbXlvYmxhc3RfZmlsZSA8LSBmaWxlLnBhdGgoZGVzZXFfZGlyLCAicm1zX215b2JsYXN0X2Rlc2VxX3Jlc3VsdHMudHN2IikKYGBgCgoKTm93IHRoYXQgd2UndmUgZGVmaW5lZCBvdXIgZmlsZSBwYXRocywgd2UgY2FuIHJlYWQgc29tZSBvZiBpdCBpbi4KCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBgZGVzZXFfbXlvYmxhc3RfZmlsZWAgVFNWIGFuZCBzYXZlIGl0IHRvIGBkZXNlcV9teW9ibGFzdF9yZXN1bHRzYC4KV2UnbGwgbmVlZCB0aGlzIGxhdGVyIHdoZW4gd2UgY29tcGFyZSBtZXNvZGVybSBERSByZXN1bHRzIHRvIG15b2JsYXN0IERFIHJlc3VsdHMuCgpgYGB7ciByZWFkIG15b2JsYXN0IFRTViwgc29sdXRpb24gPSBUUlVFfQojIHJlYWQgaW4gbXlvYmxhc3QgZGUgcmVzdWx0cyBmb3IgbGF0ZXIKZGVzZXFfbXlvYmxhc3RfcmVzdWx0cyA8LSByZWFkcjo6cmVhZF90c3YoZGVzZXFfbXlvYmxhc3RfZmlsZSkKYGBgCgpOZXh0LCByZWFkIGluIHRoZSBTQ0UgZmlsZSBhbmQgc2F2ZSBpdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBgcm1zX3NjZWAuClRoZW4sIHByaW50IGl0IG91dCB0byBoYXZlIGEgbG9vayBhdCBpdHMgY29udGVudHMuCgpgYGB7ciByZWFkIGlucHV0IHNjZSwgc29sdXRpb24gPSBUUlVFfQojIHJlYWQgaW4gc2NlIG9iamVjdApybXNfc2NlIDwtIHJlYWRSRFMocm1zX3NjZV9maWxlKQojIHByaW50IHNjZQpybXNfc2NlCmBgYAoKQmVmb3JlIHdlIHByb2NlZWQsIHRoaXMgU0NFIG9iamVjdCBjb250YWlucyBhIGBjb2xEYXRhYCBjb2x1bW4gYHNpemVGYWN0b3JgIHRoYXQgd2FzIHByZXZpb3VzbHkgY2FsY3VsYXRlZCB3aGVuIGNvdW50cyB3ZXJlIG5vcm1hbGl6ZWQgZHVyaW5nIGluaXRpYWwgcHJvY2Vzc2luZy4KVGhpcyBjb2x1bW4gd2lsbCBlbmQgdXAgY29uZmxpY3Rpbmcgd2l0aCBzb21lIG9mIGBERVNlcTJgJ3Mgb3BlcmF0aW9ucywgc28gd2Ugd2FudCB0byByZW1vdmUgdGhpcyBjb2x1bW4gYmVmb3JlIHByb2NlZWRpbmcuCihOb3RlIHRoYXQgaWYgeW91IHNraXBwZWQgdGhpcyBzdGVwLCBgREVTZXEyYCB3b3VsZCBlbmQgdXAgcHJpbnRpbmcgYW4gZXJyb3IgYWJvdXQgdGhpcyBvZmZlbmRpbmcgY29sdW1uIHNwZWNpZmljYWxseSwgc28geW91IHdvdWxkIGhhdmUgZm91bmQgb3V0IGxhdGVyIHRoYXQgaXQgaGFzIHRvIGdvISkKCmBgYHtyIHJlbW92ZSBzaXplRmFjdG9ycyBjb2x1bW59CiMgU2V0IGNvbHVtbiB2YWx1ZSB0byBgTlVMTGAgaW4gb3JkZXIgdG8gcmVtb3ZlIGl0CnJtc19zY2Ukc2l6ZUZhY3RvciA8LSBOVUxMCmBgYAoKQXMgd2Ugc2F3IGR1cmluZyBpbnN0cnVjdGlvbiwgdGhpcyBTQ0UgY29udGFpbnMgdGhyZWUgRVJNUyBhbmQgdGhyZWUgQVJNUyBzYW1wbGVzLgpMZXQncyByZW1pbmQgb3Vyc2VsdmVzIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbCB0eXBlcyBiZWZvcmUgcHJvY2VlZGluZy4KVG8gZm9jdXMgb24gdGhlIHR1bW9yIGNlbGwgdHlwZXMgKGFzIHdlIHdpbGwgYmUgZG9pbmcgZm9yIHRoZSByZXN0IG9mIHRoZSBub3RlYm9vayEpLCB3ZSdsbCBhbHNvIHN1YnNldCB0aGUgYHJtc19zY2VgIG9iamVjdCB0byBqdXN0IHR1bW9yIGNlbGxzLgoKYGBge3IgZmlsdGVyIHR1bW9yIHNjZX0KdHVtb3JfY2VsbHMgPC0gcm1zX3NjZSRjZWxsdHlwZV9icm9hZCB8PgogICMgRmluZCB3aGljaCBjZWxsIHR5cGVzIHN0YXJ0IHdpdGggIlR1bW9yXyIKICBzdHJpbmdyOjpzdHJfc3RhcnRzKCJUdW1vcl8iKSB8PgogICMgUGlwZSBpbnRvIGB3aGljaCgpYCB0byBlbnN1cmUgdGhhdCBgTkFgIHZhbHVlcyBhcmUgcHJvcGVybHkgaGFuZGxlZAogIHdoaWNoKCkKCiMgZmlsdGVyIHRoZSBTQ0Ugb2JqZWN0IHRvIGtlZXAgb25seSB0aG9zZSBjZWxscwpybXNfc2NlIDwtIHJtc19zY2VbLCB0dW1vcl9jZWxsc10KYGBgCgpMZXQncyBub3cgcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSByZW1haW5pbmcgdHVtb3IgY2VsbCB0eXBlczoKCmBgYHtyIGNlbGx0eXBlIGJhcnBsb3R9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB2ZXJzaW9uIG9mIHRoZSBjb2xEYXRhIHRhYmxlIGZvciBwbG90dGluZwpjb2xkYXRhX2RmIDwtIGNvbERhdGEocm1zX3NjZSkgfD4KICBhcy5kYXRhLmZyYW1lKCkKCiMgTWFrZSBhIGJhcnBsb3Qgb2YgY2VsbCB0eXBlIHByb3BvcnRpb25zIGFjcm9zcyBzYW1wbGVzIGFuZCBkaWFnbm9zZXMKZ2dwbG90KGNvbGRhdGFfZGYpICsKICBhZXMoeCA9IHNhbXBsZSwKICAgICAgZmlsbCA9IGNlbGx0eXBlX2Jyb2FkKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCAjIEFkZCBhIHRoaW4gb3V0bGluZSBmb3IgZWFzaWVyIHZpZXdpbmcKICAgICAgICAgICBsd2QgPSAwLjE1KSArCiAgZmFjZXRfd3JhcCh2YXJzKGRpYWdub3Npc19ncm91cCksCiAgICAgICAgICAgICAjIG9ubHkgcHV0IHJlbGV2YW50IHNhbXBsZXMgaW4gZWFjaCBwYW5lbAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeCIpICsKICAjIFNwZWNpZnkgdGhlbWUgYW5kIG1vZGlmeSB4LWF4aXMgdGV4dCBzaXplCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBgVHVtb3JfTXlvYmxhc3RgIGlzIG11Y2ggaGlnaGVyIHRoYW4gb3RoZXIgY2VsbCB0eXBlcywgaW5jbHVkaW5nIHRoZSBgVHVtb3JfTWVzb2Rlcm1gIHN1YnBvcHVsYXRpb25zIHdlJ2xsIGJlIHRlc3RpbmcgaGVyZS4KV2UgYWxzbyBzZWUgdGhhdCBFUk1TIHNhbXBsZXMgaGF2ZSBtb3JlIGBUdW1vcl9NZXNvZGVybWAgY2VsbHMgYW5kIGZld2VyIGBUdW1vcl9NeW9jeXRlYCBjZWxscyBjb21wYXJlZCB0byBBUk1TIHNhbXBsZXMsIGNvbnNpc3RlbnQgd2l0aCBQYXRlbCBfZXQgYWwuXydzIGZpbmRpbmdzIHRoYXQgQVJNUyBzYW1wbGVzIHRlbmRlZCB0byBoYXZlIG1vcmUgY2VsbCB0eXBlcyBmcm9tIGxhdGVyIG15b2dlbmVzaXMgZGV2ZWxvcG1lbnRhbCBzdGFnZXMgY29tcGFyZWQgdG8gRVJNUy4KQXMgd2UgcHJvY2VlZCB0byB0aGUgcHNldWRvLWJ1bGtpbmcgc3RlcHMgZm9yIHRoZSBgVHVtb3JfTWVzb2Rlcm1gIHN1YnBvcHVsYXRpb24sIHdlIHdpbGwgY2hlY2sgdGhhdCB3ZSBoYXZlIHN1ZmZpY2llbnQgc2FtcGxlIHNpemVzIHRvIHBlcmZvcm0gb3VyIGFuYWx5c2lzLgoKCiMjIyBQZXJmb3JtIHBzZXVkby1idWxraW5nCgpUbyBiZWdpbiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvbiBzaW5nbGUtY2VsbCBkYXRhLCB3ZSBuZWVkIHRvIHBzZXVkby1idWxrIHRoZSBkYXRhIHRvICJjb2xsYXBzZSIgZ3JvdXBzIG9mIGludGVyZXN0LgpBcyBkdXJpbmcgaW5zdHJ1Y3Rpb24sIHdlIHdhbnQgdG8gcGVyZm9ybSBwc2V1ZG8tYnVsa2luZyBhY3Jvc3MgdHdvIGdyb3VwaW5nczogYGNlbGx0eXBlX2Jyb2FkYCBhbmQgYHNhbXBsZWAuCgpUbyBhY2NvbXBsaXNoIHRoaXMsIGNyZWF0ZSBhIHZhcmlhYmxlIGNhbGxlZCBgcGJfZ3JvdXBzYCwgd2hpY2ggd2lsbCBiZSBhIGBEYXRhRnJhbWVgIHRoYXQgY29udGFpbnMgb25seSB0aGUgYGNvbERhdGFgIGNvbHVtbnMgb2YgaW50ZXJlc3QgZm9yIHBzZXVkby1idWxraW5nLgoKYGBge3IgY3JlYXRlIHBiX2dyb3Vwcywgc29sdXRpb24gPSBUUlVFfQojIGZpcnN0IHN1YnNldCB0aGUgY29sZGF0YQojIHRvIG9ubHkgaGF2ZSB0aGUgY29sdW1ucyB3ZSBjYXJlIGFib3V0IGZvciBwc2V1ZG8tYnVsa2luZwpwYl9ncm91cHMgPC0gY29sRGF0YShybXNfc2NlKVssYygiY2VsbHR5cGVfYnJvYWQiLCAic2FtcGxlIildCmBgYAoKTmV4dCwgdXNlIHRoaXMgdmFyaWFibGUgdG8gY3JlYXRlIHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSB3aXRoIHRoZSBgc2N1dHRsZTo6YWdncmVnYXRlQWNyb3NzQ2VsbHMoKWAgZnVuY3Rpb24sIGFuZCBzYXZlIGl0IHRvIGBwYl9zY2VgLgpJbiBhZGRpdGlvbiwgeW91J2xsIHdhbnQgdG8gZW5zdXJlIGNvbHVtbiBuYW1lcyBhcmUgcHJvcGVybHkgYWRkZWQgYmFjayBpbnRvIHRoaXMgbmV3IHBzZXVkby1idWxrZWQgb2JqZWN0LgoKYGBge3IgY3JlYXRlIHBiX3NjZSwgc29sdXRpb24gPSBUUlVFfQojIGNyZWF0ZSBhIG5ldyBTQ0Ugb2JqZWN0IHRoYXQgY29udGFpbnMKIyB0aGUgcHNldWRvLWJ1bGtlZCBjb3VudHMgYWNyb3NzIHRoZSBwcm92aWRlZCBncm91cHMKcGJfc2NlIDwtIHNjdXR0bGU6OmFnZ3JlZ2F0ZUFjcm9zc0NlbGxzKAogIHJtc19zY2UsCiAgaWQgPSBwYl9ncm91cHMKKQojIEFkZCBjb2x1bW4gbmFtZXMgZm9ybWF0dGVkIGFzIGB7Y2VsbHR5cGV9X3tzYW1wbGV9YCBiYWNrIHRvIGBwYl9zY2VgCmNvbG5hbWVzKHBiX3NjZSkgPC0gZ2x1ZTo6Z2x1ZSgie3BiX3NjZSRjZWxsdHlwZV9icm9hZH1fe3BiX3NjZSRzYW1wbGV9IikKCiMgQ2hlY2sKcGJfc2NlCmBgYAoKTGV0J3Mgc2VlIHdoYXQgd2UndmUgZ290IQpQcmludCBvdXQgYHBiX3NjZWAgYmVsb3cuCkhvdyBtYW55ICJjZWxscyIgZG8gd2UgaGF2ZSBub3cgYWZ0ZXIgcHNldWRvLWJ1bGtpbmc/CgpgYGB7ciBwcmludCBwYl9zY2V9CnBiX3NjZQpgYGAKCkFzIHdlIGhhdmUgc2Vlbiwgd2hlbiB3ZSBwZXJmb3JtZWQgdGhlIHBzZXVkby1idWxraW5nLCBhIG5ldyBjb2x1bW4gYG5jZWxsc2Agd2FzIGFkZGVkIHRvIHRoZSBgY29sRGF0YWAgdGFibGUuClRoaXMgaW5mb3JtYXRpb24gdGVsbHMgdXMgaG93IG1hbnkgY2VsbHMgd2VyZSBjb2xsYXBzZWQgaW50byB0aGUgZ3JvdXBpbmcuCgpUbyBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLCB3ZSdkIGxpa2UgdG8gc2VlIGEgc3VpdGFibGUgbnVtYmVyIG9mIGNlbGxzIHByZXNlbnQgaW4gZWFjaCBncm91cC4KRHVyaW5nIGluc3RydWN0aW9uLCB3ZSBjaG9zZSBhIHRocmVzaG9sZCBvZiBgbmNlbGxzID49IDEwYCB0byBpbmNsdWRlIGEgZ3JvdXAgaW4gYW5hbHlzaXMuCkxldCdzIGRvIHRoZSBzYW1lIGZpbHRlcmluZyBoZXJlLCBhbmQgdGhlbiBjaGVjayB0byBzZWUgdGhhdCB3ZSBoYXZlIGVub3VnaCBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgdG8gcGVyZm9ybSBhbmFseXNpcy4KUmVjYWxsLCB3ZSBhcmUgc3RhcnRpbmcgd2l0aCB0aHJlZSBFUk1TIGFuZCB0aHJlZSBBUk1TIHNhbXBsZXMsIGFuZCB3ZSdsbCBuZWVkIGF0IGxlYXN0IHRocmVlIHNhbXBsZXMgZnJvbSBlYWNoIGRpYWdub3NpcyBncm91cCBmb3Igb3VyIGNlbGwgdHlwZSBvZiBpbnRlcmVzdCBmb3Igc3RhdGlzdGljYWwgdmlhYmlsaXR5LgoKVG8gdGhpcyBlbmQsIGxldCdzIHRha2UgdHdvIGZpbHRlcmluZyBzdGVwcyBpbiB0aGUgbmV4dCBjaHVuaywgc2F2aW5nIHRoZSBvdXRwdXQgdG8gYSBuZXcgb2JqZWN0IGNhbGxlZCBgbWVzb2Rlcm1fcGJfc2NlYDoKCi0gV2UnbGwgZmlsdGVyIHRoZSBgcGJfc2NlYCB0byByZXRhaW4gb25seSBncm91cGluZ3Mgd2hlcmUgYG5jZWxscyA+PSAxMGAKLSBXZSdsbCBhbHNvIGZpbHRlciB0aGUgYHBiZV9zY2VgIG9iamVjdCB0byByZXRhaW4gb25seSBvdXIgY2VsbCB0eXBlIG9mIGludGVyZXN0LCBgVHVtb3JfTWVzb2Rlcm1gLgoKYGBge3IgZmlsdGVyIHBiX3NjZSwgc29sdXRpb24gPSBUUlVFfQojIEZpcnN0IGZpbHRlciB0byBrZWVwIG9ubHkgYG5jZWxscyA+PSAxMGAgYW5kIHNhdmUgdG8gYG1lc29kZXJtX3BiX3NjZWAKbWVzb2Rlcm1fcGJfc2NlIDwtIHBiX3NjZVssIHdoaWNoKHBiX3NjZSRuY2VsbHMgPj0gMTApXQoKIyBUaGVuIGZpbHRlciB0byBrZWVwIG9ubHkgYFR1bW9yX01lc29kZXJtYCBhbmQgc2F2ZSB0byB0aGUgc2FtZSB2YXJpYWJsZQojIEZpcnN0IGNyZWF0ZSBsb2dpY2FsIHZlY3RvcgptZXNvZGVybV9jZWxscyA8LSBwYl9zY2UkY2VsbHR5cGVfYnJvYWQgPT0gIlR1bW9yX01lc29kZXJtIgoKbWVzb2Rlcm1fcGJfc2NlIDwtIG1lc29kZXJtX3BiX3NjZVssbWVzb2Rlcm1fY2VsbHNdCmBgYAoKTm93IGxldCdzIGNoZWNrIHRoYXQgd2UgaGF2ZSByZXRhaW5lZCBlbm91Z2ggc2FtcGxlcyB0byBwcm9jZWVkLgpJbiB0aGUgZm9sbG93aW5nIGNodW5rLCB1c2UgdGhlIGB0YWJsZSgpYCBmdW5jdGlvbiBvbiB0aGUgZmlsdGVyZWQgU0NFJ3MgYGRpYWdub3Npc19ncm91cGAgdG8gY291bnQgdXAgdGhlIG51bWJlciBvZiBlYWNoIGRpYWdub3NpcyAtIGhvcGVmdWxseSB3ZSBzdGlsbCBoYXZlIGF0IGxlYXN0IHRocmVlIChwc2V1ZG8tYnVsa2VkKSBzYW1wbGVzIG9mIGVhY2ghCgpgYGB7ciB0YWJsZSBkaWFnbm9zaXNfZ3JvdXAsIHNvbHV0aW9uID0gVFJVRX0KIyBNYWtlIGEgdGFibGUgb2YgdGhlIGRpYWdub3NpcyBncm91cCBjb3VudHMKdGFibGUobWVzb2Rlcm1fcGJfc2NlJGRpYWdub3Npc19ncm91cCkKYGBgCgpJbmRlZWQsIHdlIGhhdmUgZW5vdWdoIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyB0byBwcm9jZWVkLCBzbyBsZXQncyBoZWFkIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKIyMgUGFydCBCOiBQZXJmb3JtIERFIGFuYWx5c2lzIG9uIHRoZSB0dW1vciBtZXNvZGVybSBjZWxsIHN1YnBvcHVsYXRpb24KCk9mZiB0byAodGhlKSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAocmFjZXMpIQoKTm93IHRoYXQgd2UgaGF2ZSBwcmVwYXJlZCBvdXIgb2JqZWN0IGZvciBhbmFseXNpcywgd2UncmUgcmVhZHkgdG8gaWRlbnRpZnkgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBpbiBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLgoKRmlyc3QsIGxldCdzIGRlZmluZSBhIGBERVNlcURhdGFTZXRgIG9iamVjdCBjYWxsZWQgYGRlc2VxX29iamVjdGAgZnJvbSB0aGUgYG1lc29kZXJtX3BiX3NjZWAsIHNwZWNpZnlpbmcgdGhlIGRlc2lnbiBmb3JtdWxhIGFjcm9zcyB0aGUgYGRpYWdub3Npc19ncm91cGAgdmFyaWFibGUuCgpgYGB7ciBkZWZpbmUgZGVzZXEgb2JqZWN0LCBzb2x1dGlvbiA9IFRSVUV9CiMgY3JlYXRlIHRoZSBERVNlcURhdGFTZXQgb2JqZWN0CmRlc2VxX29iamVjdCA8LSBERVNlcURhdGFTZXQoCiAgbWVzb2Rlcm1fcGJfc2NlLAogIGRlc2lnbiA9IH5kaWFnbm9zaXNfZ3JvdXAKICApCmBgYAoKTm93LCB1c2UgdGhlIGNvbnZlbmllbmNlIGZ1bmN0aW9uIGBERVNlcTI6OkRFU2VxKClgIHRvIHBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGFzIHdlIHNhdyBkdXJpbmcgaW5zdHJ1Y3Rpb24sIHNhdmluZyB0aGUgcmVzdWx0IGJhY2sgdG8gYGRlc2VxX29iamVjdGAuCgpgYGB7ciBwZXJmb3JtIGRlIGFuYWx5c2lzLCBzb2x1dGlvbiA9IFRSVUV9CiMgcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgpkZXNlcV9vYmplY3QgPC0gREVTZXEyOjpERVNlcShkZXNlcV9vYmplY3QpCmBgYAoKQmVmb3JlIHByb2NlZWRpbmcsIHdlJ2xsIHdhbnQgdG8gcXVpY2tseSBjaGVjayB0aGF0IHRoZSBtb2RlbCB3YXMgYSBkZWNlbnQgZml0IHRvIHRoZSBkYXRhLCBhbmQgd2UgY2FuIGRvIHRoaXMgYnkgdmlzdWFsaXppbmcgdGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIHVzaW5nIHRoZSBmdW5jdGlvbiBgREVTZXEyOjpwbG90RGlzcEVzdHMoKWAuCk1ha2UgdGhpcyBwbG90IGluIHRoZSBjaHVuayBiZWxvdzoKCmBgYHtyIHBsb3QgZGlzcGVyc2lvbnMsIHNvbHV0aW9uID0gVFJVRX0KIyBwbG90IGRpc3BlcnNpb25zCkRFU2VxMjo6cGxvdERpc3BFc3RzKGRlc2VxX29iamVjdCkKYGBgCgpIb3cgZG9lcyB0aGlzIHBsb3QgbG9vayB0byB5b3U/CkEgd2VsbC1maXR0ZWQgbW9kZWwgc2hvdWxkIHNob3cgZGVjcmVhc2luZyBkaXNwZXJzaW9ucyBhcyB0aGUgbWVhbiBhbG9uZyB0aGUgeC1heGlzIGluY3JlYXNlcy4KRG8geW91IHNlZSB0aGF0IHRyZW5kIGhlcmU/CgpUbyBiZSBhYmxlIHRvIGludGVycHJldCBvdXIgcmVzdWx0cyBhbmQgY29tcGFyZSBnZW5lcywgd2UgbmV4dCBuZWVkIHRvIGFwcGx5IGEgc2hyaW5rYWdlIHByb2NlZHVyZSB0aGF0IHdpbGwgY29ycmVjdCBleHByZXNzaW9uIGxldmVscyB0byBhdm9pZCBvdmVyZXN0aW1hdGluZyBmb2xkIGNoYW5nZS4KRG8gdGhpcyBpbiB0aGUgbmV4dCBjaHVuayBieSB0YWtpbmcgdGhlIGZvbGxvd2luZyBzdGVwczoKCi0gRXh0cmFjdCB0aGUgcmVzdWx0cyB3aGljaCBwYXNzIGEgdGhyZXNob2xkIG9mIGBhbHBoYSA9IDAuMDVgIGZyb20gYGRlc2VxX29iamVjdGAuClNhdmUgdGhpcyBkYXRhIGZyYW1lIHRvIGEgdmFyaWFibGUgYGRlc2VxX3Jlc3VsdHNgLgotIFBlcmZvcm0gdGhlIHNocmlua2FnZSBwcm9jZWR1cmUgdXNpbmcgYERFU2VxMjo6bGZjU2hyaW5rKClgLCBzYXZpbmcgcmVzdWx0cyB0byBgc2hyaW5rX3Jlc3VsdHNgLgpNYWtlIHN1cmUgdG8gbG9vayBhdCB5b3VyIG1vZGVsIGZpcnN0IHRvIGZpbmQgdGhlIHBvc2l0aW9uIG9mIHRoZSByZXN1bHQgY29lZmZpY2llbnQgdG8gcHJvdmlkZSB0byBgREVTZXEyOjpsZmNTaHJpbmsoKWAuCgpgYGB7cn0KIyBHRVc6IEknbSBjdXJpb3VzIGFib3V0IDIgdGhpbmdzOgogICMgMSkgVG8gd2hhdCBleHRlbnQgY2VsbCBwcm9wb3J0aW9uIC8gY291bnQgaW1wYWN0cyBERSByZXN1bHRzIC0tIHN1YnNldCB0byA0MDAgcmFuZG9tIGNlbGxzIChUdW1vcl9tZXNvZGVybSkgcGVyIGRpYWdub3NpcyBncm91cCwgYW5kIHJlcGVhdCBhbmFseXNpcwogICMgMikgVG8gd2hhdCBleHRlbnQgc2hyaW5rYWdlIGltcGFjdHMgREUgcmVzdWx0cyAtLSByZXBlYXQgdmlzdWFsaXphdGlvbnMgb24gdGhlIG5vbi1zaHJ1bmsgcmVzdWx0cwpgYGAKCmBgYHtyIHBlcmZvcm0gc2hyaW5rYWdlLCBzb2x1dGlvbiA9IFRSVUV9CiMgRXh0cmFjdCByZXN1bHRzIGludG8gYSBkYXRhIGZyYW1lCmRlc2VxX3Jlc3VsdHMgPC0gREVTZXEyOjpyZXN1bHRzKGRlc2VxX29iamVjdCwgYWxwaGEgPSAwLjA1KQoKIyBCZWZvcmUgcGVyZm9ybWluZyB0aGUgc2hyaW5rYWdlIHByb2NlZHVyZSwgY2hlY2sKIyAgaGVyZSB0byBmaW5kIHdoZXJlIHRoZSByZXN1bHQgY29lZmZpY2llbnQgaXMKREVTZXEyOjpyZXN1bHRzTmFtZXMoZGVzZXFfb2JqZWN0KQoKIyBQZXJmb3JtIHRoZSBzaHJpbmthZ2UgcHJvY2VkdXJlLCBzYXZpbmcgdG8gYHNocmlua19yZXN1bHRzYApzaHJpbmtfcmVzdWx0cyA8LSBERVNlcTI6OmxmY1NocmluaygKICBkZXNlcV9vYmplY3QsCiAgcmVzID0gZGVzZXFfcmVzdWx0cywKICBjb2VmID0gMiwKICB0eXBlID0gImFwZWdsbSIKICApCgojIENvbXBhcmUKaGVhZChkZXNlcV9yZXN1bHRzKQpoZWFkKHNocmlua19yZXN1bHRzKQpgYGAKCkdvIGFoZWFkIGFuZCBwcmludCB0aGUgcmVzdWx0aW5nIGBzaHJpbmtfcmVzdWx0c2A6CgpgYGB7ciBwcmludCBzaHJpbmtfcmVzdWx0c30Kc2hyaW5rX3Jlc3VsdHMKYGBgCgpCZWZvcmUgcHJvY2VlZGluZywgaXQncyB3b3J0aCBkaXNjdXNzaW5nIGhvdyB0byBpbnRlcnByZXQgdGhlc2UgYGxvZzJGb2xkQ2hhbmdlYCB2YWx1ZXMgaW4gdGVybXMgb2YgQVJNUyBhbmQgRVJNUyBzYW1wbGVzLgpJbiB0aGlzIGNhc2UsIHdlIGRpZCBub3Qgc2V0IGEgcGFydGljdWxhciBmYWN0b3Igb3JkZXIgZm9yIHRoZSBgZGlhZ25vc2lzX2dyb3VwYCB2YXJpYWJsZSB0aGF0IHdlIHRlc3RlZCBvbiwgc28gUiBieSBkZWZhdWx0IHNldCB0aGUgb3JkZXIgYWxwaGFiZXRpY2FsbHkuCldlIGNhbiBzZWUgdGhpcyBiZWxvdyB1c2luZyBgbGV2ZWxzKClgLgoKYGBge3IgY2hlY2sgZGlhZ25vc2lzX2dyb3VwIGxldmVsc30KIyBTZWUgdGhlIG9yZGVyIG9mIHRoZSBkaWFnbm9zaXNfZ3JvdXAgY29sdW1uCmxldmVscyhtZXNvZGVybV9wYl9zY2UkZGlhZ25vc2lzX2dyb3VwKQpgYGAKClRoaXMgbWVhbnMgdGhhdCBhbGwgcmVzdWx0cyBhcmUgX3JlbGF0aXZlIHRvIEFSTVNfLgpOZWdhdGl2ZSBgbG9nMkZvbGRDaGFuZ2VgIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IEVSTVMgaGFzIGxvd2VyIGV4cHJlc3Npb24gcmVsYXRpdmUgdG8gQVJNUy4KUG9zaXRpdmUgYGxvZzJGb2xkQ2hhbmdlYCB2YWx1ZXMgaW5kaWNhdGUgdGhhdCBFUk1TIGhhcyBoaWdoZXIgZXhwcmVzc2lvbiByZWxhdGl2ZSB0byBBUk1TLgoKU29tZXRoaW5nIGVsc2Ugd2UnbGwgbmVlZCB0byBoZWxwIHVzIGludGVycHJldCB0aGVzZSByZXN1bHRzIGFyZSBzb21lIGdlbmUgbmFtZXMsIHNpbmNlIG5vdCBhbGwgb2YgdXMgaGF2ZSBoYWQgYSBjaGFuY2UgdG8gbWVtb3JpemUgdGhlIEVuc2VtYmwgSURzIHlldC4KU28sIGxldCdzIG5vdyBhZGQgaW4gYSBgZ2VuZV9zeW1ib2xgIGNvbHVtbiB0byBvdXIgcmVzdWx0cyBkYXRhIGZyYW1lIChgc2hyaW5rX3Jlc3VsdHNgKS4KVGhlc2Ugc3ltYm9scyB3ZXJlIG9yaWdpbmFsbHkgc3RvcmVkIGluIHRoZSBTQ0UncyBgcm93RGF0YWAgdGFibGUgc28gd2UgY2FuIHNpbXBseSBncmFiIHRoZSBuYW1lcyBmcm9tIHRoZXJlLgpJbiB0aGUgY2h1bmsgYmVsb3csIHdlJ2xsIGRvIHNvbWUgd3JhbmdsaW5nIHRvIGNvbWJpbmUgYHNocmlua19yZXN1bHRzYCB3aXRoIHRoZSBnZW5lIG5hbWVzLCBzYXZpbmcgdGhlIGZpbmFsIHJlc3VsdCB0byBgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c2AuCgpgYGB7ciBhZGQgZ2VuZV9zeW1ib2x9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmcm9tIHRoZSBTQ0Ugcm93RGF0YQpzY2Vfcm93ZGF0YV9kZiA8LSByb3dEYXRhKG1lc29kZXJtX3BiX3NjZSkgfD4KICAgIyBDb252ZXJ0IHRvIGEgZGF0YSBmcmFtZSBhbmQgbW92ZSB0aGUgcm93bmFtZXMKICAgIyAgaW50byBhIG5ldyBjb2x1bW4gY2FsbGVkIGBlbnNlbWJsX2lkYAogICB0aWJibGU6OmFzX3RpYmJsZShyb3duYW1lcyA9ICJlbnNlbWJsX2lkIikKCiMgSm9pbiB0aGUgREVTZXEgcmVzdWx0cyB3aXRoIGBzY2Vfcm93ZGF0YV9kZmAKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cyA8LSBzaHJpbmtfcmVzdWx0cyB8PgogIHRpYmJsZTo6YXNfdGliYmxlKHJvd25hbWVzID0gImVuc2VtYmxfaWQiKSB8PgogICMgam9pbiB3aXRoIHNjZV9yb3dkYXRhX2RmIGJ5IEVuc2VtYmwgaWRzCiAgZHBseXI6OmxlZnRfam9pbihzY2Vfcm93ZGF0YV9kZiwgYnkgPSAiZW5zZW1ibF9pZCIpIHw+CiAgIyByZW9yZGVyIGNvbHVtbnMgc28gZ2VuZV9zeW1ib2wgY29tZXMgcmlnaHQgYWZ0ZXIgZW5zZW1ibF9pZAogICMgIHRvIG1ha2UgdGhlIHRhYmxlIGVhc2llciB0byByZWFkCiAgZHBseXI6OnJlbG9jYXRlKGdlbmVfc3ltYm9sLCAuYWZ0ZXIgPSAiZW5zZW1ibF9pZCIpCgojIFByaW50IHRoZSBmaW5hbCByZXN1bHRzIGRhdGEgZnJhbWUKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cwpgYGAKCgpMZXQncyBub3cgZmlsdGVyIHRvIG9ubHkgb3VyIHNpZ25pZmljYW50IHJlc3VsdHMsIHVzaW5nIGEgY3V0b2ZmIG9mIF9hZGp1c3RlZCBQLXZhbHVlXyBvZiAwLjA1LCBhbmQgc2F2ZSB0aGUgbmV3IGRhdGEgZnJhbWUgdG8gYGRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnYAoKYGBge3IgZmlsdGVyIG1lc29kZXJtIHNpZ25pZmljYW50LCBzb2x1dGlvbiA9IFRSVUV9CiMgRmlsdGVyIHJlc3VsdHMgdG8gcGFkaiA8PSAwLjA1CmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHNbd2hpY2goZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cyRwYWRqIDw9IDAuMDUpLF0KIyBQcmludCB0aGUgcmVzdWx0CmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnCmBgYAoKYGBge3J9CiMgQW5vdGhlciBtZXRob2QgdXNpbmcgZHBseXIKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c19zaWcyIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHMgfD4KICBkcGx5cjo6ZmlsdGVyKHBhZGogPD0gMC4wNSkKCmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnMgpgYGAKCiMjIyBWaXN1YWxpemUgcmVzdWx0cwoKTGV0J3MgdmlzdWFsaXplIHNvbWUgb2Ygb3VyIHJlc3VsdHMhCgpUbyBzdGFydCwgdXNlIHRoZSBgRW5oYW5jZWRWb2xjYW5vYCBwYWNrYWdlIHRvIG1ha2UgYSB2b2xjYW5vIHBsb3Qgb2YgdGhlIHJlc3VsdHMuCkZvciB0aGlzIHBsb3QsIHNwZWNpZnkgdGhhdCBwb2ludHMgYmUgbGFiZWxlZCB3aXRoIHRoZSBnZW5lIHN5bWJvbCBhbmQgdXNlIHRoZSBkZWZhdWx0IFAtdmFsdWUgY3V0b2ZmIG9mIGAxZS0wNWAuCkZlZWwgZnJlZSB0byBhcHBseSBhbnkgYWRkaXRpb25hbCBzdHlsZXMgeW91IHdhbnQgKHRoZSBwbG90IGl0c2VsZiBpcyBhIGBnZ3Bsb3QyYCBvYmplY3QhKS4KSGludDogTWFrZSBzdXJlIHRvIHBsb3QgdGhlIGBkZXNlcV9tZXNvZGVybV9yZXN1bHRzYCBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgYm90aCBzaWduaWZpY2FudCBhbmQgbm90IHNpZ25pZmljYW50IHJlc3VsdHMuCgpgYGB7ciBtZXNvZGVybSB2b2xjYW5vIHBsb3QsIHNvbHV0aW9uID0gVFJVRX0KIyBNYWtlIGFuIChlbmhhbmNlZCkgdm9sY2FubyBwbG90CnNocmlua2FnZV92b2xjYW5vX3Bsb3QgPC0gRW5oYW5jZWRWb2xjYW5vOjpFbmhhbmNlZFZvbGNhbm8oCiAgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cywKICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwKICB5ID0gInBhZGoiLAogIGxhYiA9IGRlc2VxX21lc29kZXJtX3Jlc3VsdHMkZ2VuZV9zeW1ib2wsCiAgcEN1dG9mZiA9IDFlLTA1LCAjIHAgdmFsdWUgY3V0b2ZmIChkZWZhdWx0KQogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICBsYWJTaXplID0gMywKICB0aXRsZSA9ICJERVNlcTIgVHVtb3IgTWVzb2Rlcm0gKFdpdGggU2hyaW5rYWdlKSIsICMgbm8gdGl0bGUKICBzdWJ0aXRsZSA9IE5VTEwsICMgb3Igc3VidGl0bGUKICBjYXB0aW9uID0gTlVMTCwgIyBvciBjYXB0aW9uCikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpzaHJpbmthZ2Vfdm9sY2Fub19wbG90Cmdnc2F2ZShnbHVlOjpnbHVlKCJ7ZGVzZXFfZGlyfS92b2xjYW5vX3Nocmlua2FnZS5wbmciKSwgZHBpPTQwMCwgaGVpZ2h0PTUsIHdpZHRoPTUpCmBgYAoKTGV0J3MgcGljayBzb21lIGhpZ2hseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgZnJvbSB0aGlzIHZvbGNhbm8gcGxvdCBhbmQgc2hvdyB0aGVtIG9uIGEgVU1BUC4KV2UnbGwgcGljayBvbmUgZ2VuZSB0aGF0IHNob3dzIGhpZ2hlciBBUk1TIGV4cHJlc3Npb24gKG5lZ2F0aXZlIGxvZzIgZm9sZCBjaGFuZ2VzKSwgYW5kIG9uZSB0aGF0IHNob3dzIGhpZ2hlciBFUk1TIGV4cHJlc3Npb24gKHBvc2l0aXZlIGxvZzIgZm9sZCBjaGFuZ2VzKS4KVG8gcGxvdCB0aGVzZSBvbiBhIFVNQVAsIHdlJ2xsIG5lZWQgdG8ga25vdyB0aGVpciBFbnNlbWJsIGlkcywgc28gbGV0J3MgZ2V0IHRoYXQgaW5mb3JtYXRpb24gZmlyc3Q6CgoKYGBge3IgZmluZCBlbnNlbWJsIGlkc30KIyBEZWZpbmUgc29tZSBnZW5lcyB0byB2aXN1YWxpemUKYXJtc19nZW5lIDwtICJITUNOMiIKZXJtc19nZW5lIDwtICJQTEVLSEIyIgoKIyBVc2UgZHBseXIgdG8gZ2V0IHRoZSBhc3NvY2lhdGVkIEVuc2VtYmwgaWRzCmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnIHw+CiAgIyBGaW5kIHJvd3MgdGhhdCBjb250YWluIG91ciBnZW5lIHN5bWJvbHMKICBkcGx5cjo6ZmlsdGVyKGdlbmVfc3ltYm9sICVpbiUgYyhlcm1zX2dlbmUsIGFybXNfZ2VuZSkpIHw+CiAgIyBTZWxlY3Qgb25seSB0aGVzZSB0d28gY29sdW1ucwogIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9pZCwgZ2VuZV9zeW1ib2wpCmBgYAoKVG8gbWFrZSBvdXIgVU1BUCBvZiBtZXNvZGVybSBjZWxscywgd2UgYWxzbyB3YW50IHRvIGZpbHRlciBvdXIgb3JpZ2luYWwgYHJtc19zY2VgIHRvIGNvbnRhaW4gb25seSBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLgooQW4gYXNpZGUhCllvdSBtYXkgcmVjYWxsIHdlIHByZXZpb3VzbHkgY3JlYXRlZCBgbWVzb2Rlcm1fcGJfc2NlYCB0aGF0IHdhcyBmaWx0ZXJlZCB0byB0dW1vciBtZXNvZGVybSBjZWxscywgc28gd2h5IGNhbid0IHdlIHVzZSB0aGF0IG9iamVjdCB0byBtYWtlIGEgVU1BUD8KVGhhdCBvYmplY3Qgd2FzIGRlcml2ZWQgZnJvbSB0aGUgX3BzZXVkby1idWxrZWRfIFNDRSwgc28gaXQgZG9lcyBub3QgY29udGFpbiBpbmRpdmlkdWFsIGNlbGxzLgpUaGVyZWZvcmUsIHdlIG5lZWQgdG8gZ28gYmFjayB0byB0aGUgYmVnaW5uaW5nIGFuZCB3b3JrIHdpdGggYHJtc19zY2VgLikKCmBgYHtyIGZpbHRlciBybXNfc2NlLCBzb2x1dGlvbiA9IFRSVUV9CiMgRmlsdGVyIHJtc19zY2UgdG8gY29udGFpbiBvbmx5IFR1bW9yX01lc29kZXJtIGNlbGxzCm1lc29kZXJtX3NjZSA8LSBybXNfc2NlWyx3aGljaChybXNfc2NlJGNlbGx0eXBlX2Jyb2FkID09ICdUdW1vcl9NZXNvZGVybScpXQpgYGAKCldlIHNob3VsZCBhbHNvIGNoZWNrIHRoZSBuYW1lcyBvZiBvdXIgcmVkdWNlZCBkaW1lbnNpb25zIHRvIHNlZSB3aGF0IHRoZSBVTUFQIG1hdHJpeCBpcyBjYWxsZWQgc28gdGhhdCB3ZSBjYW4gcGxvdCBpdDoKCmBgYHtyIGZpbmQgdW1hcCBuYW1lfQojIFByaW50IHJlZHVjZWREaW1OYW1lcyBvZiBtZXNvZGVybV9zY2UKcmVkdWNlZERpbU5hbWVzKG1lc29kZXJtX3NjZSkKYGBgCgpXZSdkIGxpa2UgdG8gcGxvdCB0aGUgaW50ZWdyYXRlZCBVTUFQLCBzbyB3ZSdsbCBwbG90IHRoZSBgImZhc3Rtbm5fVU1BUCJgIG1hdHJpeCBiZWxvdy4KCkkgbWFwLCB5b3UgbWFwLCB3ZSBhbGwgbWFwIGZvciBtYXAgbWFwcyEKRmlyc3QsIGxldCdzIHZpc3VhbGl6ZSB0aGUgZ2VuZSB3aXRoIEVuc2VtYmwgaWQgYEVOU0cwMDAwMDE0ODM1N2AgdGhhdCBpcyB1cHJlZ3VsYXRlZCBpbiBBUk1TLgpGb3IgdGhpcyBwbG90LCBjb2xvciBieSBleHByZXNzaW9uIGFuZCBmYWNldCBieSBgZGlhZ25vc2lzX2dyb3VwYC4KCmBgYHtyIHVtYXAgYXJtcyBnZW5lLCBzb2x1dGlvbiA9IFRSVUV9CiMgUGxvdCBVTUFQIHNob3dpbmcgRU5TRzAwMDAwMTQ4MzU3IGV4cHJlc3Npb24gYWNyb3NzIGRpYWdub3NpcyBncm91cHMKc2NhdGVyOjpwbG90VU1BUCgKICBtZXNvZGVybV9zY2UsCiAgZGltcmVkID0gImZhc3Rtbm5fVU1BUCIsCiAgY29sb3JfYnkgPSAiRU5TRzAwMDAwMTQ4MzU3IiwKICBvdGhlcl9maWVsZHMgPSAiZGlhZ25vc2lzX2dyb3VwIgopICsKICB0aGVtZV9idygpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGRpYWdub3Npc19ncm91cCkpCmBgYAoKSG93IGRvZXMgdGhpcyBwbG90IGxvb2sgdG8geW91PwpEb2VzIGl0IHNlZW0gdG8gcmVmbGVjdCB0aGUgc3RhdGlzdGljYWwgcmVzdWx0IHRoYXQgQVJNUyBoYXMgaGlnaGVyIGV4cHJlc3Npb24sIG9uIGF2ZXJhZ2UsIGNvbXBhcmVkIHRvIEVSTVM/CgpMZXQncyBkbyB0aGUgc2FtZSB3aXRoIHRoZSBnZW5lIHRoYXQgaXMgdXByZWd1bGF0ZWQgaW4gRVJNUywgYW5kIGFnYWluIHRoaW5rIGFib3V0IHdoZXRoZXIgdGhlIHBsb3Qgc2hvd3MgdGhlIHRyZW5kcyB5b3UgZXhwZWN0ZWQgYmFzZWQgb24gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMhCgpgYGB7cn0KIyBHRVc6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGZhY2V0X3dyYXAoKSBhbmQgZmFjZXRfZ3JpZCgpID8KYGBgCgpgYGB7ciBtZXNvZGVybSB1bWFwIHVwcmVnfQojIFBsb3QgVU1BUCBzaG93aW5nIEVOU0cwMDAwMDExNTc2MiBleHByZXNzaW9uIGFjcm9zcyBkaWFnbm9zaXMgZ3JvdXBzCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0obWVzb2Rlcm1fc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0bW5uX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gIkVOU0cwMDAwMDExNTc2MiIsCiAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfZmllbGRzID0gImRpYWdub3Npc19ncm91cCIpICsKICBmYWNldF93cmFwKHZhcnMoZGlhZ25vc2lzX2dyb3VwKSkgKwogIHRoZW1lX2J3KCkKYGBgCgoKCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGlzIGRhdGEgdXNpbmcgdmlvbGluIHBsb3RzIHdpdGggdGhlIGBzY2F0ZXI6OnBsb3RFeHByZXNzaW9uKClgIGZ1bmN0aW9uLCBhbGxvd2luZyB1cyB0byBjb21wYXJlIGV4cHJlc3Npb24gYWNyb3NzIHN1YnBvcHVsYXRpb25zIG9mIGludGVyZXN0IChoZXJlLCB0dW1vciBjZWxsIHR5cGVzIGFuZCBkaWFnbm9zaXMgZ3JvdXBzKS4KCkxldCdzIG1ha2UgdGhhdCBwbG90IGJlbG93IGZyb20gdGhlIGBybXNfc2NlYCBvYmplY3QsIHdoaWNoIHlvdSdsbCByZWNhbGwgd2UgcHJldmlvdXNseSBmaWx0ZXJlZCB0byBvbmx5IHR1bW9yIGNlbGxzLgpVc2UgYGZhY2V0X2dyaWQoKWAgaW4geW91ciBwbG90IGJlbG93IHRvIHNob3cgc2VwYXJhdGUgcGFuZWxzIGZvciBlYWNoIGNvbWJpbmF0aW9uIG9mIGNlbGwgdHlwZSBhbmQgZGlhZ25vc2lzIGdyb3VwLgoKYGBge3IgcGxvdEV4cHJlc3Npb24gcGxvdCwgc29sdXRpb24gPSBUUlVFfQojIERlZmluZSBhIHZlY3RvciBvZiBFbnNlbWJsIGlkcyB0byBwbG90CgojIENyZWF0ZSBmYWNldGVkIHZpb2xpbiBwbG90CgpgYGAKCkJhc2VkIG9uIHRoaXMgcGxvdCwgaXQgbG9va3MgbGlrZSB0aGUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBhbW9uZyBkaWFnbm9zaXMgZ3JvdXBzIHdlIGZvdW5kIGZvciB0dW1vciBtZXNvZGVybSBjZWxscyBfY291bGRfIGJlIGFuIG92ZXJhbGwgdHJlbmQgYWNyb3NzIGNhbmNlciBjZWxsIHR5cGVzLgpJZiB3ZSB3YW50ZWQgdG8gZm9ybWFsbHkgdGVzdCB0aGlzLCB3ZSBjb3VsZCBjb25zaWRlciBhIG11bHRpdmFyaWF0ZSBtb2RlbCB0aGF0IGFjY291bnRzIGZvciBib3RoIGNlbGwgdHlwZSBhbmQgZGlhZ25vc2lzIGdyb3VwIGF0IG9uY2UsIHJhdGhlciB0aGFuIHBlcmZvcm1pbmcgYSBzZXBhcmF0ZWx5IHRlc3RpbmcgZWFjaCBjZWxsIHR5cGUuCgpMZXQncyBnbyBhaGVhZCBhbmQgc2F2ZSB0aGUgYGRlc2VxX21lc29kZXJtX3Jlc3VsdHNgIGRhdGEgZnJhbWUgdG8gb3VyIG91dHB1dCBUU1YgZmlsZS4KCmBgYHtyIGV4cG9ydCBtZXNvZGVybSByZXN1bHRzLCBzb2x1dGlvbiA9IFRSVUV9CiMgV3JpdGUgb3V0IHRoZSByZXN1bHRzIFRTViBmaWxlCgpgYGAKCgojIyBQYXJ0IEM6IENvbXBhcmUgcmVzdWx0cyB0byB0dW1vciBteW9ibGFzdCBERSBhbmFseXNpcyByZXN1bHRzCgpEdXJpbmcgaW5zdHJ1Y3Rpb24sIHdlIHBlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiB0dW1vciBteW9ibGFzdCBjZWxscyB0byBkZXRlY3QgZGlmZmVyZW5jZXMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMuCldlIG1pZ2h0IHdvbmRlciBob3cgb3VyIHJlc3VsdHMgZm9yIHR1bW9yIG1lc29kZXJtIGNlbGxzIGNvbXBhcmUgdG8gdGhvc2UgZm9yIG15b2JsYXN0cy4KQmVmb3JlIHdlIGNvbXBhcmUgc29tZSBvZiB0aGVzZSBmaW5kaW5ncywgd2UgbmVlZCB0byBiZSBhd2FyZSBvZiBhIGZldyBjYXZlYXRzOgoKLSBBcyBtZW50aW9uZWQgYWJvdmUsIGlmIHdlIHdlcmUgcmVhbGx5IGludGVyZXN0ZWQgaW4gd2hldGhlciBBUk1TIGFuZCBFUk1TIHNhbXBsZXMgaGF2ZSBkaWZmZXJlbnQgZXhwcmVzc2lvbiBfYWNyb3NzIGFueSBncm91cCBvZiBjZWxsIHR5cGVzXywgd2Ugd291bGQgaGF2ZSBsaWtlZCB0byBzdGFydCB3aXRoIGEgbXVsdGl2YXJpYXRlIG1vZGVsLgpCeSBwZXJmb3JtaW5nIHNlcGFyYXRlIHRlc3RzIGZvciBzZXBhcmF0ZSBjZWxsIHR5cGVzLCB3ZSByaXNrIG1ha2luZyBjb21wYXJpc29ucyBiYXNlZCBvbiAic2lnbmlmaWNhbmNlLCIgZm9yIGV4YW1wbGUgY29uY2x1ZGluZyAidGhpcyBnZW5lIHdhcyBzaWduaWZpY2FudCBpbiBtZXNvZGVybSBidXQgbm90IG15b2JsYXN0LCB0aGVyZWZvcmUgaXQncyBvbmx5IGltcG9ydGFudCBpbiBtZXNvZGVybSBjZWxsIHR5cGVzLiIKU2lnbmlmaWNhbmNlIGFzIGEgcHJvcGVydHkgaW4tYW5kLW9mLWl0c2VsZiByZWxpZXMgb24gYXJiaXRyYXJ5IFAtdmFsdWUgY3V0b2ZmcywgYW5kIFAtdmFsdWVzIGFjcm9zcyBkaWZmZXJlbnQgdGVzdHMgYW5kIHNhbXBsZXMgYXJlIG5vdCBjcmVhdGVkIGVxdWFsbHkhClRoaXMgbWVhbnMgeW91IGRvIG5vdCB3YW50IHRvIHJlbHkgb24gdGhlc2Uga2luZHMgb2YgY29tcGFyaXNvbnMgdG8gZHJhdyBjb25jbHVzaW9ucywgYnV0IHRoZXkgY2FuIGJlIHVzZWZ1bCBmb3IgZXhwbG9yYXRpb24sIGFzIHdlIHdpbGwgZG8gaGVyZS4KCi0gRHVyaW5nIGFsbCBvZiBvdXIgREUgdGVzdHMsIHRoZXJlJ3MgYSBnb29kIGNoYW5jZSB0aGF0IHdlIG1heSBoYXZlIGNvbW1pdHRlZCBzb21lIGxpZ2h0IGNpcmN1bGFyaXR5OgpUaGUgY2VsbCB0eXBlcyB3ZSBhcmUgdXNpbmcgZnJvbSBbUGF0ZWwgX2V0IGFsLl8gKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmRldmNlbC4yMDIyLjA0LjAwMykgd2VyZSBkZXRlcm1pbmVkIGZyb20gZXhwcmVzc2lvbiBkYXRhLgpCdXQgdGhlbiB3ZSB1c2VkIHRoZXNlIGV4cHJlc3Npb24tZGVyaXZlZCBjZWxsIHR5cGVzIHRvIGZpbHRlciBmb3IgREUgdGVzdGluZywgYW5kIHRoZW4gd2UgdGVzdGVkIGZvciBkaWZmZXJlbmNlcyBpbiwgeW91IGd1ZXNzZWQgaXQsIGV4cHJlc3Npb24hCkl0J3Mgbm90IG5lY2Vzc2FyaWx5IHBvc3NpYmxlIHRvIGF2b2lkIHRoaXMgdHJhcCAoc2luY2Ugd2UgbmVlZCB0byB1c2UgX3NvbWV0aGluZ18gdG8gYW5ub3RhdGUgY2VsbCB0eXBlcyksIGJ1dCBpdCdzIGltcG9ydGFudCB0byBiZSBhd2FyZSB0aGF0IHRoaXMgaXMgYSBjb25mb3VuZGluZyBmYWN0b3IgYWNyb3NzIG91ciByZXN1bHRzLgoKQmVhcmluZyB0aGVzZSBjYXZlYXRzIGluIG1pbmQsIGxldCdzIGdvIGFoZWFkIGFuZCBjb21wYXJlIHJlc3VsdHMgYSBiaXQuCkF0IHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBub3RlYm9vaywgd2UgcmVhZCBpbiB0aGUgdHVtb3IgbXlvYmxhc3QgREUgcmVzdWx0cyBhbmQgc2F2ZWQgaXQgYXMgYGRlc2VxX215b2JsYXN0X3Jlc3VsdHNgLgpMZXQncyByZW1pbmQgb3Vyc2VsdmVzIHdoYXQgdGhpcyBkYXRhIGZyYW1lIGxvb2tzIGxpa2U6CgoKYGBge3IgcHJpbnQgZGVzZXFfbXlvYmxhc3RfcmVzdWx0c30KaGVhZChkZXNlcV9teW9ibGFzdF9yZXN1bHRzKQpgYGAKClRoZSBkYXRhIGZyYW1lIGhhcyB0aGUgc2FtZSBjb2x1bW5zIGFzIG91ciBtZXNvZGVybSByZXN1bHRzIHN0b3JlZCBpbiBgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c2AuClRvIGNvbXBhcmUgcmVzdWx0cyBtb3JlIGVhc2lseSwgd2UnZCBsaWtlIHRvIGhhdmUgdGhlbSBhbGwgaW4gYSBzaW5nbGUgZGF0YSBmcmFtZSBmb3JtYXR0ZWQgdG8gZmFjaWxpdGF0ZSBjb21wYXJpc29uLgpGb3IgZXhhbXBsZSwgd2UgY2FuIG1ha2UgYSBkYXRhIGZyYW1lIHRoYXQgaGFzIGNvbHVtbnMgZm9yIGVhY2ggdGVzdCdzIFAtdmFsdWVzIGFuZCBsb2cyLWZvbGQgY2hhbmdlcyBzbyB3ZSBjYW4gZGlyZWN0bHkgY29tcGFyZSB0aGUgcmVzdWx0cyBmb3IgZWFjaCBnZW5lLgpMZXQncyB3cmFuZ2xlIHRoYXQgdG9nZXRoZXI6CgpgYGB7ciBjb21iaW5lIHRlc3QgcmVzdWx0c30KIyBzZWxlY3QgZnJvbSBhbmQgcmVuYW1lIG15b2JsYXN0IHJlc3VsdHMKbXlvYmxhc3RfZGYgPC0gZGVzZXFfbXlvYmxhc3RfcmVzdWx0cyB8PgogIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9pZCwKICAgICAgICAgICAgICAgIGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgbXlvYmxhc3RfcGFkaiA9IHBhZGosCiAgICAgICAgICAgICAgICBteW9ibGFzdF9sb2cyRm9sZENoYW5nZSA9IGxvZzJGb2xkQ2hhbmdlKQoKIyBzZWxlY3QgJiByZW5hbWUgbWVzb2Rlcm0gcmVzdWx0Cm1lc29kZXJtX2RmIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHMgfD4KICBkcGx5cjo6c2VsZWN0KGVuc2VtYmxfaWQsCiAgICAgICAgICAgICAgICBnZW5lX3N5bWJvbCwKICAgICAgICAgICAgICAgIG1lc29kZXJtX3BhZGogPSBwYWRqLAogICAgICAgICAgICAgICAgbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UgPSBsb2cyRm9sZENoYW5nZSkKCiMgQ29tYmluZSBteW9ibGFzdCBhbmQgbWVzb2Rlcm0gcmVzdWx0cwpkZXNlcV9yZXN1bHRzX2FsbCA8LSBkcGx5cjo6aW5uZXJfam9pbihteW9ibGFzdF9kZiwgbWVzb2Rlcm1fZGYpCgojIFByaW50IHRoZSBjb21iaW5lZCByZXN1bHQgZGF0YSBmcmFtZQpkZXNlcV9yZXN1bHRzX2FsbApgYGAKCldlIGNhbiBub3cgZGlyZWN0bHkgY29tcGFyZSByZXN1bHRzIG9uIGEgcGVyLWdlbmUgYmFzaXMhCk9uZSB3YXkgd2UgY2FuIGV4cGxvcmUgdGhlIHJlc3VsdCBkaWZmZXJlbmNlcyBpcyB3aXRoIGEgc2NhdHRlcnBsb3Qgc2hvd2luZyBsb2cyLWZvbGQgY2hhbmdlcyBmb3IgZWFjaCBjZWxsIHR5cGUuCldlIGNhbiBhbHNvIHN0eWxlIHRoZSBwb2ludHMgdG8gYmUgY29sb3JlZCBiYXNlZCBvbiB0aGVpciBvdmVyYWxsIHNpZ25pZmljYW5jZSwgdXNpbmcgb25lIGNvbG9yIGZvciBlYWNoIG9mIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCiAgLSBCb3RoIGNlbGwgdHlwZXMgYXJlIHNpZ25pZmljYW50CiAgLSBPbmx5IG15b2JsYXN0IHR1bW9yIGNlbGxzIGFyZSBzaWduaWZpY2FudAogIC0gT25seSBtZXNvZGVybSB0dW1vciBjZWxscyBhcmUgc2lnbmlmaWNhbnQKICAtIE5laXRoZXIgY2VsbCB0eXBlIGlzIHNpZ25pZmljYW50CgpDb2xvcmluZyBwb2ludHMgYnkgc2lnbmlmaWNhbmNlIGluIGBnZ3Bsb3QyYCB3aWxsIHJlcXVpcmUgYSBiaXQgbW9yZSB3cmFuZ2xpbmcsIGZvciB3aGljaCB3ZSdsbCB1c2UgdGhlIGZ1bmN0aW9uIFtgZHBseXI6OmNhc2Vfd2hlbigpYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9jYXNlX3doZW4uaHRtbCkuCldlIHByb3ZpZGUgdGhpcyBmdW5jdGlvbiB3aXRoIGEgc2V0IG9mIGxvZ2ljYWwgZXhwcmVzc2lvbnMgYW5kIGVhY2ggYXNzaWduZWQgdmFsdWUgaXMgZGVzaWduYXRlZCBieSBgfmAuClRoZSBleHByZXNzaW9ucyBhcmUgZXZhbHVhdGVkIGluIG9yZGVyLCBzdG9wcGluZyBhdCB0aGUgX2ZpcnN0XyBvbmUgdGhhdCBldmFsdWF0ZXMgYXMgYFRSVUVgIGFuZCByZXR1cm5pbmcgdGhlIGFzc29jaWF0ZWQgdmFsdWUuCldlJ2xsIHVzZSBgZHBseXI6OmNhc2Vfd2hlbigpYCB0byBoZWxwIHVzIGNyZWF0ZSBhIG5ldyBgc2lnbmlmaWNhbmNlX3R5cGVgIGNvbHVtbiBpbiBvdXIgZGF0YSBmcmFtZSB0byBjb250YWluIHRoaXMgaW5mb3JtYXRpb24uCgpgYGB7ciBhZGQgc2lnbmlmaWNhbmNlIGNhdGVnb3JpZXN9CmRlc2VxX3Jlc3VsdHNfYWxsIDwtIGRlc2VxX3Jlc3VsdHNfYWxsIHw+CiAgIyBDcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBzaWduaWZpY2FuY2VfdHlwZQogIGRwbHlyOjptdXRhdGUoc2lnbmlmaWNhbmNlX3R5cGUgPSBkcGx5cjo6Y2FzZV93aGVuKAogICAgIyBVc2UgdmFsdWUgIkJvdGgiIGlmIGJvdGggY29tcGFyaXNvbnMgYXJlIHNpZ25pZmljYW50CiAgICBteW9ibGFzdF9wYWRqIDw9IDAuMDUgJiBtZXNvZGVybV9wYWRqIDw9IDAuMDUgfiAiQm90aCIsCiAgICAjIFVzZSB2YWx1ZSAiTXlvYmxhc3Qgb25seSIgaWYgb25seSBteW9ibGFzdCBpcyBzaWduaWZpY2FudAogICAgbXlvYmxhc3RfcGFkaiA8PSAwLjA1IH4gIk15b2JsYXN0IG9ubHkiLAogICAgIyBVc2UgdmFsdWUgIk1lc29kZXJtIG9ubHkiIGlmIG9ubHkgbWVzb2Rlcm0gaXMgc2lnbmlmaWNhbnQKICAgIG1lc29kZXJtX3BhZGogPD0gMC4wNSB+ICJNZXNvZGVybSBvbmx5IiwKICAgICMgRGVmYXVsdCBjYXNlOiBuZWl0aGVyIGNvbXBhcmlzb24gaXMgc2lnbmlmaWNhbnQKICAgICMgIFRoaXMgd2lsbCBjYXB0dXJlIGFsbCByZW1haW5pbmcgcm93cyBub3QgY292ZXJlZCBieQogICAgIyAgdGhlIHByZXZpb3VzIGNvbmRpdGlvbnMKICAgIFRSVUUgfiAiTmVpdGhlciIKICApKSB8PgogICMgTW92ZSB0aGUgbmV3IGNvbHVtbiB0byB0aGUgZnJvbnQgb2YgdGhlIGRhdGEgZnJhbWUKICAjICBzbyB3ZSBjYW4gZWFzaWx5IHNlZSBpdCB3aGVuIHByaW50aW5nCiAgZHBseXI6OnNlbGVjdChzaWduaWZpY2FuY2VfdHlwZSwgZXZlcnl0aGluZygpKQoKIyBQcmludCB0aGUgdXBkYXRlZCBkYXRhIGZyYW1lCmRlc2VxX3Jlc3VsdHNfYWxsCmBgYAoKTGV0J3MgaGF2ZSBhIHF1aWNrIGxvb2sgYXQgdGhlIG5ldyBgc2lnbmlmaWNhbmNlX3R5cGVgIHZhcmlhYmxlIGJ5IG1ha2luZyBhIGB0YWJsZSgpYCBvZiBpdHMgdmFsdWVzOgoKYGBge3IgdGFibGUgc2lnbmZpY2FuY2VfdHlwZSwgc29sdXRpb24gPSBUUlVFfQojIE1ha2UgYSB0YWJsZSBvZiBzaWduaWZpY2FuY2UgdHlwZXMKCmBgYAoKTW9zdCBnZW5lcyBhcmUgbm90IHNpZ25pZmljYW50IGluIGVpdGhlciBjb21wYXJpc29uLCBhbmQgdGhlIG15b2JsYXN0IHRlc3QgZGV0ZWN0ZWQgbW9yZSBzaWduaWZpY2FudCBnZW5lcyBjb21wYXJlZCB0byB0aGUgbWVzb2Rlcm0gdGVzdC4KQnV0IHRoZXJlIGlzIHN0aWxsIHF1aXRlIGEgYml0IG9mIG92ZXJsYXAgaW4gdGhlIGdlbmVzIGlkZW50aWZpZWQgYXMgc2lnbmlmaWNhbnQgaW4gYm90aCB0ZXN0cyEKCk5vdyB3ZSBjYW4gbWFrZSBvdXIgc2NhdHRlcnBsb3Q6CgoKYGBge3IgbG9nZmMgc2NhdHRlcnBsb3R9CiMgUGxvdCBsb2cyIGZvbGQgY2hhbmdlcyBhZ2FpbnN0IGVhY2ggb3RoZXIKZ2dwbG90KGRlc2VxX3Jlc3VsdHNfYWxsLAogICAgICAgYWVzKHggPSBteW9ibGFzdF9sb2cyRm9sZENoYW5nZSwKICAgICAgICAgICB5ID0gbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UsCiAgICAgICAgICAgY29sb3IgPSBzaWduaWZpY2FuY2VfdHlwZSkpICsKICAjIFN0eWxpbmcgdG8gc2VlIHBvaW50cwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwKICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgIyBVc2UgdGhlIGNvbG9yYmxpbmQtZnJpZW5kbHkgdmlyaWRpcyBjb2xvciBzY2FsZQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZCgpICsKICAjIFNldCBwbG90IGxhYmVscwogIGxhYnMoCiAgICB4ID0gIk15b2JsYXN0IGxvZzIgZm9sZCBjaGFuZ2UiLAogICAgeSA9ICJNZXNvZGVybSBsb2cyIGZvbGQgY2hhbmdlIiwKICAgIGNvbG9yID0gIlNpZ25maWNhbmNlIFR5cGUiCiAgKSArCiAgdGhlbWVfYncoKSArCiAgIyBNYWtlIGxlZ2VuZCBwb2ludHMgbGFyZ2VyIGFuZCBlYXNpZXIgdG8gc2VlCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIpKSkgKwogICMgU2V0IGVxdWFsIHggYW5kIHkgc2NhbGVzIGZvciBlYXNpZXIgY29tcGFyaXNvbiBiZXR3ZWVuIGF4ZXMKICBjb29yZF9lcXVhbCgpCmBgYAoKCldoYXQga2luZCBvZiB0cmVuZHMgZG8gd2Ugc2VlIGhlcmU/CgotIFRoZSBtaWRkbGUgb2YgdGhlIHBsb3QgY2xvc2UgdG8gY29vcmRpbmF0ZXMgYCgwLDApYCBoYXMgYSBsYXJnZSBjbG91ZCBvZiB5ZWxsb3cgIk5laXRoZXIiIHBvaW50cywgd2hpY2ggbWFrZXMgc2Vuc2UuCi0gQWxvbmcgZWFjaCBsaW5lIGB4ID0gMGAgb3IgYHkgPSAwYCwgd2Ugc2VlIHN0cmlwcyBvZiBwb2ludHMgZm9yIGdlbmVzIHRoYXQgYXJlIG9ubHkgc2lnbmlmaWNhbnQgZm9yIG9ubHkgb25lIGNlbGwgdHlwZS4KRm9yIGV4YW1wbGUsIHRoZSBibHVlICJNZXNvZGVybSBvbmx5IiBwb2ludHMgbW9zdGx5IGZvbGxvdyBgeCA9IDBgOyBmb3IgdGhlc2UgcG9pbnRzLCB0aGUgYXNzb2NpYXRlZCBteW9ibGFzdCBsb2cyLWZvbGQgY2hhbmdlcyBhcmUgYWxsIGF0IGFib3V0IDAuCi0gRmluYWxseSwgd2UgaGF2ZSB0aGUgcHVycGxlICJCb3RoIiBwb2ludHMsIHdoaWNoIGFyZSBwcmVzZW50IGluIGVhY2ggcXVhZHJhbnQgZmFydGhlciBhd2F5IGZyb20gYCgwLDApYCwgZGVlbWVkIHNpZ25pZmljYW50IGluIGJvdGggREUgdGVzdHMuCk1vc3Qgb2YgdGhlc2UgcG9pbnRzIGFyZSBpbiB0aGUgdG9wIHJpZ2h0IGFuZCBib3R0b20gbGVmdCBxdWFkcmFudHMsIGNvcnJlc3BvbmRpbmcgdG8gY29uZGl0aW9ucyB3aGVyZSBfYm90aF8gY2VsbCB0eXBlcyBzaG93IHNpZ25pZmljYW50IHVwcmVndWxhdGlvbiBvciBkb3ducmVndWxhdGlvbiwgcmVzcGVjdGl2ZWx5LgpIb3dldmVyLCB3ZSBkbyBhbHNvIHNlZSBzb21lIHB1cnBsZSBwb2ludHMsIGFsYmVpdCBmZXdlciwgaW4gdGhlIHRvcCBsZWZ0IGFuZCBib3R0b20gcmlnaHQgcXVhZHJhbnRzIHdoaWNoIHJlcHJlc2VudCBnZW5lcyB3aGVyZSBib3RoIGNlbGwgdHlwZXMgc2hvdyBzaWduaWZpY2FudCBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGJ1dCBpbiBfZGlmZmVyZW50XyBkaXJlY3Rpb25zLgoKVG8gc3RhcnQgZXhwbG9yaW5nLCBsZXQncyBwaWNrIG91dCBzb21lIG9mIHRoZSBnZW5lcyB3aXRoIHRoZSBsYXJnZXN0IGZvbGQtY2hhbmdlcyBmcm9tIHRoZSB0b3AgcmlnaHQgYW5kIGJvdHRvbSBsZWZ0IHF1YWRyYW50cyBvZiB0aGUgcGxvdC4KQmFzZWQgb24gdGhlIHBsb3QsIGl0IGxvb2tzIGxpa2Ugd2UnbGwgZmluZCBhIHNldCBvZiBzaGFyZWQgdXByZWd1bGF0ZWQgZ2VuZXMgYnkgbG9va2luZyBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMgdGhhdCBhcmUgZ3JlYXRlciB0aGFuIDggZm9yIGJvdGggY2VsbCB0eXBlcywgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYnkgbG9va2luZyBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMgbGVzcyB0aGFuICAtMTAgZm9yIG15b2JsYXN0cyBhbmQgLTcgZm9yIG1lc29kZXJtIGNlbGxzLgoKCmBgYHtyIGZpbmQgcXVhZHJhbnQgZ2VuZXN9CiMgRmluZCBnZW5lcyBpbiB0b3AgcmlnaHQgYW5kIGJvdHRvbSBsZWZ0IHF1YWRyYW50cwpkZXNlcV9yZXN1bHRzX2FsbCB8PgogIGRwbHlyOjpmaWx0ZXIoCiAgICAjIEZpbmQgZWl0aGVyIHRoZSBoaWdobHkgdXByZWd1bGF0ZWQgZ2VuZXM6CiAgICAobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UgPj0gOCAmIG1lc29kZXJtX2xvZzJGb2xkQ2hhbmdlID49IDgpIHwKICAgICAjIE9SLCB0aGUgaGlnaGx5IGRvd25yZWd1bGF0ZWQgZ2VuZXM6CiAgICAobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UgPD0gLTEwICYgbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UgPD0tNykKICApIHw+CiAgIyBhcnJhbmdlIG9uIG15b2JsYXN0X2xvZzJGb2xkQ2hhbmdlCiAgZHBseXI6OmFycmFuZ2UobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UpCmBgYAoKU29tZXRoaW5nIHlvdSdsbCBzZWUgaW4gdGhlc2UgcmVzdWx0cyBhcmUgc29tZSBwcmV0dHkgZGlmZmVyZW50IFAtdmFsdWVzIGJldHdlZW4gY2VsbCB0eXBlcyAoYWxzbyBub3RlIHRoYXQgdGhlIGBOQWAgZ2VuZXMgaGVyZSBhcmUgbG5jUk5BcyB3aXRoIG5vIGZvcm1hbGx5IGFzc2lnbmVkIGdlbmUgc3ltYm9sKS4KU3BlY2lmaWNhbGx5LCB0aGUgbXlvYmxhc3QgUC12YWx1ZXMgYXJlIGFsbCAzLTcgb3JkZXJzIG9mIG1hZ25pdHVkZSBsb3dlciB0aGFuIHRoZWlyIG1lc29kZXJtIGNvdW50ZXJwYXJ0cywgd2hpY2ggX21heV8gYmUgYSByZXN1bHQgb2YgdGhlIHJlbGF0aXZlbHkgaGlnaGVyIHNhbXBsZSBzaXplIGZvciBteW9ibGFzdCB0ZXN0cyAtIGxhcmdlciBzYW1wbGUgc2l6ZXMgbGVhZCB0byBtb3JlIGV4dHJlbWUgUC12YWx1ZXMuCkltcG9ydGFudGx5LCB3ZSBkbyBfbm90XyB3YW50IHRvIGNvbXBhcmUgdGhlc2UgUC12YWx1ZXMgZGlyZWN0bHkgYW5kIGNvbmNsdWRlIHRoYXQgYSBnaXZlbiBnZW5lIHdhcyAibW9yZSBvciBsZXNzIHNpZ25pZmljYW50IiBpbiBvbmUgY2VsbCB0eXBlIG9yIGFub3RoZXIsIHNpbmNlIFAtdmFsdWVzIGNhbm5vdCBiZSBjb21wYXJlZCBhY3Jvc3MgdGVzdHMgKGFnYWluLCBmb3IgYSBtb3JlIHJvYnVzdCBhc3Nlc3NtZW50IG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB1c2UgYSBtdWx0aXZhcmlhdGUgbW9kZWwgdGhhdCBhY2NvdW50cyBmb3IgY2VsbCB0eXBlcyEpLgoKVG8gd3JhcCB1cCwgZmVlbCBmcmVlIHRvIHBlcmZvcm0gc29tZSBxdWljayB2aXN1YWxpemF0aW9uIG9mIHNvbWUgb2YgdGhlc2UgZ2VuZXMhCgpBbHRlcm5hdGl2ZWx5IChvciBpbiBhZGRpdGlvbiEpLCB5b3UgY2FuIHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgZ2VuZXMgdGhhdCByZWd1bGF0ZSBteW9nZW5lc2lzLCBhcyBzaG93biBpbiB0aGUgZmlndXJlIGZyb20gUGF0ZWwgX2V0IGFsLl8gYXQgdGhlIHRvcCBvZiB0aGlzIG5vdGVib29rLgpMZXQncyBoYXZlIGEgcXVpY2sgbG9vayBhdCB0aG9zZSByZXN1bHRzOgoKYGBge3IgbXlvZ2VuZXNpcyBnZW5lc30KIyBEZWZpbmUgdmVjdG9yIG9mIG15b2dlbmVzaXMgZ2VuZSBzeW1ib2xzCm15b2dlbmVzaXNfc3ltYm9scyA8LSBjKCJNRU9YMiIsICJQQVgzIiwgIk1ZRjUiLCAiTVNDIiwgIk1ZT0ciLCAiTUVGMkMiKQoKIyBQcmludCB0aGUgREUgcmVzdWx0cyBmb3IgdGhvc2UgZ2VuZXMKZGVzZXFfcmVzdWx0c19hbGwgfD4KICBkcGx5cjo6ZmlsdGVyKGdlbmVfc3ltYm9sICVpbiUgbXlvZ2VuZXNpc19zeW1ib2xzKSB8PgogICMgc29ydCBpbiBvcmRlciBvZiB0aGUgYG15b2dlbmVzaXNfc3ltYm9sc2AgdmVjdG9yCiAgZHBseXI6OmFycmFuZ2UobWF0Y2goZ2VuZV9zeW1ib2wsIG15b2dlbmVzaXNfc3ltYm9scykpCmBgYAoKWW91IGNhbiBtYWtlIHBsb3RzIHdlJ3ZlIHNlZW4gbGlrZS4uLgoKLSBBIGZhY2V0ZWQgVU1BUCB3aXRoIGBzY2F0ZXI6OnBsb3RVTUFQKClgIHNob3dpbmcgZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbCB0eXBlIGFuZCBkaWFnbm9zaXMgZ3JvdXAKLSBBIHZpb2xpbiBwbG90IHdpdGggYHNjYXRlcjo6cGxvdEV4cHJlc3Npb24oKWAgZmFjZXRlZCBhY3Jvc3MgY2VsbCB0eXBlIGFuZCBkaWFnbm9zaXMgZ3JvdXAKCgpgYGB7ciBkaXkgcGxvdHMsIHNvbHV0aW9uID0gVFJVRX0KIyBVc2UgdGhpcyBjaHVuayAob3IgYWRkIG1vcmUgY2h1bmtzISkgdG8gbWFrZSB5b3VyIG93biBwbG90cwoKIyB2ZWN0b3Igb2YgZ2VuZSBzeW1ib2xzIG5hbWVkIGJ5IEVuc2VtYmwgaWQKCmBgYAoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK